Engauge Digitizer  2
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 
31 const 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 
58 {
59 }
60 
61 void FittingWindow::calculateCurveFitAndStatistics ()
62 {
63  FittingStatistics fittingStatistics;
64 
65  double mse = 0, rms = 0, rSquared = 0;
66  fittingStatistics.calculateCurveFitAndStatistics (unsigned (maxOrder ()),
67  m_pointsConvenient,
68  m_coefficients,
69  mse,
70  rms,
71  rSquared,
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 
111 void FittingWindow::closeEvent(QCloseEvent * /* event */)
112 {
113  LOG4CPP_INFO_S ((*mainCat)) << "FittingWindow::closeEvent";
114 
116 }
117 
118 void FittingWindow::createWidgets (MainWindow *mainWindow)
119 {
120  QWidget *widget = new QWidget;
121  setWidget (widget);
122 
123  QGridLayout *layout = new QGridLayout;
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 
192 void 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 
200 int FittingWindow::maxOrder () const
201 {
202  return m_cmbOrder->currentData().toInt();
203 }
204 
205 void 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 
215 void 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 
245  QStandardItem *item = new QStandardItem (termString);
246  m_model->setItem (row, COLUMN_POLYNOMIAL_TERMS, item);
247  }
248 }
249 
250 void FittingWindow::slotCmbOrder(int /* index */)
251 {
252  refreshTable ();
253 }
254 
255 void 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 
278  ENGAUGE_CHECK_PTR (curve);
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 
311 QTableView *FittingWindow::view () const
312 {
313  return dynamic_cast<QTableView*> (m_view);
314 }
FittingModel.h
WindowTable.h
FittingWindow::closeEvent
virtual void closeEvent(QCloseEvent *event)
Catch close event so corresponding menu item in MainWindow can be updated accordingly.
Definition: FittingWindow.cpp:111
COORD_SCALE_LOG
Definition: CoordScale.h:16
MainWindowModel.h
Points
QList< Point > Points
Definition: Points.h:12
Transformation::transformIsDefined
bool transformIsDefined() const
Transform is defined when at least three axis points have been digitized.
Definition: Transformation.cpp:335
FittingStatistics::calculateCurveFitAndStatistics
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.
Definition: FittingStatistics.cpp:111
MainWindowModel::significantDigits
int significantDigits() const
Get method for significant digits.
Definition: MainWindowModel.cpp:243
Document::curveForCurveName
const Curve * curveForCurveName(const QString &curveName) const
See CurvesGraphs::curveForCurveNames, although this also works for AXIS_CURVE_NAME.
Definition: Document.cpp:335
CmdMediator.h
FittingWindow::clear
virtual void clear()
Clear stale information.
Definition: FittingWindow.cpp:102
FittingWindow::signalFittingWindowClosed
void signalFittingWindowClosed()
Signal that this QDockWidget was just closed.
Transformation
Affine transformation between screen and graph coordinates, based on digitized axis points.
Definition: Transformation.h:30
EngaugeAssert.h
Curve::points
const Points points() const
Return a shallow copy of the Points.
Definition: Curve.cpp:451
CurveStyle.h
Curve
Container for one set of digitized Points.
Definition: Curve.h:32
WindowTable
Table view class with support for both drag-and-drop and copy-and-paste.
Definition: WindowTable.h:16
MainWindowModel
Model for DlgSettingsMainWindow.
Definition: MainWindowModel.h:29
Transformation.h
ENGAUGE_CHECK_PTR
#define ENGAUGE_CHECK_PTR(ptr)
#endif
Definition: EngaugeAssert.h:26
MainWindow
Main window consisting of menu, graphics scene, status bar and optional toolbars as a Single Document...
Definition: MainWindow.h:90
Point::posScreen
QPointF posScreen() const
Accessor for screen position.
Definition: Point.cpp:404
Logger.h
FittingWindow::doCopy
virtual void doCopy()
Copy the current selection to the clipboard.
Definition: FittingWindow.cpp:178
FittingCurveCoefficients.h
CmdMediator::document
Document & document()
Provide the Document to commands, primarily for undo/redo processing.
Definition: CmdMediator.cpp:72
COLUMN_COEFFICIENTS
const int COLUMN_COEFFICIENTS
Definition: FittingWindow.cpp:31
FittingWindow::view
virtual QTableView * view() const
QTableView-based class used by child class.
Definition: FittingWindow.cpp:311
Curve::numPoints
int numPoints() const
Number of points.
Definition: Curve.cpp:432
WindowModelBase::selectionAsText
QString selectionAsText(ExportDelimiter delimiter) const
Convert the selection into exportable text which is good for text editors.
Definition: WindowModelBase.cpp:132
LOG4CPP_INFO_S
#define LOG4CPP_INFO_S(logger)
Definition: convenience.h:18
DocumentModelCoords::coordScaleYRadius
CoordScale coordScaleYRadius() const
Get method for linear/log scale on y/radius.
Definition: DocumentModelCoords.cpp:89
GeometryModel.h
COLUMN_POLYNOMIAL_TERMS
const int COLUMN_POLYNOMIAL_TERMS
Definition: FittingWindow.cpp:32
FittingStatistics.h
mainCat
log4cpp::Category * mainCat
Definition: Logger.cpp:14
MainWindow.h
Transformation::transformScreenToRawGraph
void transformScreenToRawGraph(const QPointF &coordScreen, QPointF &coordGraph) const
Transform from cartesian pixel screen coordinates to cartesian/polar graph coordinates.
Definition: Transformation.cpp:453
MainWindowModel::dragDropExport
bool dragDropExport() const
Get method for drag and drop export.
Definition: MainWindowModel.cpp:80
FittingWindow::FittingWindow
FittingWindow(MainWindow *mainWindow)
Single constructor. Parent is needed or else this widget cannot be redocked after being undocked.
Definition: FittingWindow.cpp:34
DocumentModelCoords::coordScaleXTheta
CoordScale coordScaleXTheta() const
Get method for linear/log scale on x/theta.
Definition: DocumentModelCoords.cpp:84
DocumentModelExportFormat::delimiter
ExportDelimiter delimiter() const
Get method for delimiter.
Definition: DocumentModelExportFormat.cpp:119
CmdMediator
Command queue stack.
Definition: CmdMediator.h:22
CurveConnectAs.h
Document::modelCoords
DocumentModelCoords modelCoords() const
Get method for DocumentModelCoords.
Definition: Document.cpp:695
FittingWindow.h
MAX_POLYNOMIAL_ORDER
const int MAX_POLYNOMIAL_ORDER
Definition: FittingStatistics.h:13
Curve.h
Point
Class that represents one digitized point. The screen-to-graph coordinate transformation is always ex...
Definition: Point.h:24
FittingStatistics
This class does the math to compute statistics for FittingWindow.
Definition: FittingStatistics.h:18
WindowModelBase::setDelimiter
void setDelimiter(ExportDelimiter delimiter)
Save output delimiter.
Definition: WindowModelBase.cpp:164
Document::modelExport
DocumentModelExportFormat modelExport() const
Get method for DocumentModelExportFormat.
Definition: Document.cpp:716
FittingWindow::~FittingWindow
virtual ~FittingWindow()
Definition: FittingWindow.cpp:57
FittingWindow::signalCurveFit
void signalCurveFit(FittingCurveCoefficients, double, double, bool, bool)
Signal containing coefficients from curve fit.
WindowAbstractBase
Dockable widget abstract base class.
Definition: WindowAbstractBase.h:19
FittingModel
Model for FittingWindow.
Definition: FittingModel.h:13
FittingWindow::update
virtual void update(const CmdMediator &cmdMediator, const MainWindowModel &modelMainWindow, const QString &curveSelected, const Transformation &transformation)
Populate the table with the specified Curve.
Definition: FittingWindow.cpp:255