1 /***************************************************************************
2     File                 : ExpDecayDialog.cpp
3     Project              : SciDAVis
4     --------------------------------------------------------------------
5     Copyright            : (C) 2006 by Ion Vasilief, Tilman Benkert
6     Email (use @ for *)  : ion_vasilief*yahoo.fr, thzs*gmx.net
7     Description          : Fit exponential decay dialog
8 
9  ***************************************************************************/
10 
11 /***************************************************************************
12  *                                                                         *
13  *  This program is free software; you can redistribute it and/or modify   *
14  *  it under the terms of the GNU General Public License as published by   *
15  *  the Free Software Foundation; either version 2 of the License, or      *
16  *  (at your option) any later version.                                    *
17  *                                                                         *
18  *  This program is distributed in the hope that it will be useful,        *
19  *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
20  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
21  *  GNU General Public License for more details.                           *
22  *                                                                         *
23  *   You should have received a copy of the GNU General Public License     *
24  *   along with this program; if not, write to the Free Software           *
25  *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
26  *   Boston, MA  02110-1301  USA                                           *
27  *                                                                         *
28  ***************************************************************************/
29 #include "ExpDecayDialog.h"
30 #include "Graph.h"
31 #include "ColorButton.h"
32 #include "ApplicationWindow.h"
33 #include "Fit.h"
34 #include "ExponentialFit.h"
35 
36 #include <QMessageBox>
37 #include <QLayout>
38 #include <QGroupBox>
39 #include <QPushButton>
40 #include <QLabel>
41 #include <QLineEdit>
42 #include <QComboBox>
43 #include <QCloseEvent>
44 
ExpDecayDialog(int type,QWidget * parent,Qt::WindowFlags fl)45 ExpDecayDialog::ExpDecayDialog(int type, QWidget *parent, Qt::WindowFlags fl) : QDialog(parent, fl)
46 {
47     slopes = type;
48 
49     setWindowTitle(tr("Verify initial guesses"));
50 
51     QGroupBox *gb1 = new QGroupBox();
52     QGridLayout *gl1 = new QGridLayout();
53     gl1->addWidget(new QLabel(tr("Exponential Fit of")), 0, 0);
54 
55     boxName = new QComboBox();
56     connect(boxName, SIGNAL(activated(const QString &)), this,
57             SLOT(activateCurve(const QString &)));
58     gl1->addWidget(boxName, 0, 1);
59 
60     if (type < 0)
61         dampingLabel = new QLabel(tr("Growth time"));
62     else if (type == 1)
63         dampingLabel = new QLabel(tr("Decay time"));
64     else
65         dampingLabel = new QLabel(tr("First decay time (t1)"));
66     gl1->addWidget(dampingLabel, 1, 0);
67 
68     boxFirst = new QLineEdit();
69     boxFirst->setText(tr("1"));
70     gl1->addWidget(boxFirst, 1, 1);
71 
72     if (type > 1) {
73         gl1->addWidget(new QLabel(tr("Second decay time (t2)")), 2, 0);
74 
75         boxSecond = new QLineEdit();
76         boxSecond->setText(tr("1"));
77         gl1->addWidget(boxSecond, 2, 1);
78 
79         thirdLabel = new QLabel(tr("Third decay time (t3)"));
80         gl1->addWidget(thirdLabel, 3, 0);
81 
82         boxThird = new QLineEdit();
83         boxThird->setText(tr("1"));
84         gl1->addWidget(boxThird, 3, 1);
85 
86         if (type < 3) {
87             thirdLabel->hide();
88             boxThird->hide();
89         }
90     }
91 
92     if (type <= 1) {
93         gl1->addWidget(new QLabel(tr("Amplitude")), 2, 0);
94         boxAmplitude = new QLineEdit();
95         boxAmplitude->setText(tr("1"));
96         gl1->addWidget(boxAmplitude, 2, 1);
97     }
98 
99     gl1->addWidget(new QLabel(tr("Y Offset")), 4, 0);
100     boxYOffset = new QLineEdit();
101     boxYOffset->setText(tr("0"));
102     gl1->addWidget(boxYOffset, 4, 1);
103 
104     gl1->addWidget(new QLabel(tr("Initial time")), 5, 0);
105 
106     boxStart = new QLineEdit();
107     boxStart->setText(tr("0"));
108     gl1->addWidget(boxStart, 5, 1);
109 
110     gl1->addWidget(new QLabel(tr("Color")), 6, 0);
111     btnColor = new ColorButton();
112     btnColor->setColor(QColor(Qt::red));
113     gl1->addWidget(btnColor, 6, 1);
114 
115     gb1->setLayout(gl1);
116 
117     buttonFit = new QPushButton(tr("&Fit"));
118     buttonFit->setDefault(true);
119 
120     buttonCancel = new QPushButton(tr("&Close"));
121 
122     QBoxLayout *bl1 = new QBoxLayout(QBoxLayout::TopToBottom);
123     bl1->addWidget(buttonFit);
124     bl1->addWidget(buttonCancel);
125     bl1->addStretch();
126 
127     QHBoxLayout *hlayout = new QHBoxLayout();
128     hlayout->addWidget(gb1);
129     hlayout->addLayout(bl1);
130     setLayout(hlayout);
131 
132     // signals and slots connections
133     connect(buttonFit, SIGNAL(clicked()), this, SLOT(fit()));
134     connect(buttonCancel, SIGNAL(clicked()), this, SLOT(close()));
135 }
136 
setGraph(Graph * g)137 void ExpDecayDialog::setGraph(Graph *g)
138 {
139     if (!g)
140         return;
141 
142     fitter = 0;
143     graph = g;
144 
145     boxName->addItems(graph->analysableCurvesList());
146 
147     QString selectedCurve = g->selectedCurveTitle();
148     if (!selectedCurve.isEmpty()) {
149         int index = boxName->findText(selectedCurve);
150         boxName->setCurrentIndex(index);
151     }
152     activateCurve(boxName->currentText());
153 
154     connect(graph, SIGNAL(closedGraph()), this, SLOT(close()));
155     connect(graph, SIGNAL(dataRangeChanged()), this, SLOT(changeDataRange()));
156 }
157 
activateCurve(const QString & curveName)158 void ExpDecayDialog::activateCurve(const QString &curveName)
159 {
160     QwtPlotCurve *c = graph->curve(curveName);
161     if (!c)
162         return;
163 
164     ApplicationWindow *app = (ApplicationWindow *)this->parent();
165     if (!app)
166         return;
167 
168     int precision = app->fit_output_precision;
169     double start, end;
170     graph->range(graph->curveIndex(curveName), &start, &end);
171     boxStart->setText(QString::number(qMin(start, end)));
172     boxYOffset->setText(QString::number(c->minYValue(), 'g', precision));
173     if (slopes < 2)
174         boxAmplitude->setText(QString::number(c->maxYValue() - c->minYValue(), 'g', precision));
175 }
176 
changeDataRange()177 void ExpDecayDialog::changeDataRange()
178 {
179     double start = graph->selectedXStartValue();
180     double end = graph->selectedXEndValue();
181     boxStart->setText(QString::number(qMin(start, end), 'g', 15));
182 }
183 
fit()184 void ExpDecayDialog::fit()
185 {
186     QString curve = boxName->currentText();
187     QwtPlotCurve *c = graph->curve(curve);
188     QStringList curvesList = graph->analysableCurvesList();
189     if (!c || !curvesList.contains(curve)) {
190         QMessageBox::critical(
191                 this, tr("Warning"),
192                 tr("The curve <b> %1 </b> doesn't exist anymore! Operation aborted!").arg(curve));
193         boxName->clear();
194         boxName->addItems(curvesList);
195         return;
196     }
197 
198     ApplicationWindow *app = (ApplicationWindow *)this->parent();
199     if (!app)
200         return;
201 
202     int precision = app->fit_output_precision;
203 
204     if (fitter)
205         delete fitter;
206 
207     if (slopes == 3) {
208         double x_init[7] = { 1.0,
209                              boxFirst->text().toDouble(),
210                              1.0,
211                              boxSecond->text().toDouble(),
212                              1.0,
213                              boxThird->text().toDouble(),
214                              boxYOffset->text().toDouble() };
215         fitter = new ThreeExpFit(app, graph);
216         fitter->setInitialGuesses(x_init);
217     } else if (slopes == 2) {
218         double x_init[5] = { 1.0, boxFirst->text().toDouble(), 1.0, boxSecond->text().toDouble(),
219                              boxYOffset->text().toDouble() };
220         fitter = new TwoExpFit(app, graph);
221         fitter->setInitialGuesses(x_init);
222     } else if (slopes == 1 || slopes == -1) {
223         double x_init[3] = { boxAmplitude->text().toDouble(), slopes / boxFirst->text().toDouble(),
224                              boxYOffset->text().toDouble() };
225         fitter = new ExponentialFit(app, graph, slopes == -1);
226         fitter->setInitialGuesses(x_init);
227     }
228 
229     if (fitter->setDataFromCurve(boxName->currentText(), boxStart->text().toDouble(),
230                                  c->maxXValue())) {
231         fitter->setColor(btnColor->color());
232         fitter->scaleErrors(app->fit_scale_errors);
233         fitter->setOutputPrecision(app->fit_output_precision);
234         fitter->generateFunction(app->generateUniformFitPoints, app->fitPoints);
235         fitter->fit();
236 
237         auto &results = fitter->results();
238         boxFirst->setText(QString::number(results[1], 'g', precision));
239         if (slopes < 2) {
240             boxAmplitude->setText(QString::number(results[0], 'g', precision));
241             boxYOffset->setText(QString::number(results[2], 'g', precision));
242         } else if (slopes == 2) {
243             boxSecond->setText(QString::number(results[3], 'g', precision));
244             boxYOffset->setText(QString::number(results[4], 'g', precision));
245         } else if (slopes == 3) {
246             boxSecond->setText(QString::number(results[3], 'g', precision));
247             boxThird->setText(QString::number(results[5], 'g', precision));
248             boxYOffset->setText(QString::number(results[6], 'g', precision));
249         }
250     }
251 }
252 
closeEvent(QCloseEvent * e)253 void ExpDecayDialog::closeEvent(QCloseEvent *e)
254 {
255     if (fitter) {
256         ApplicationWindow *app = (ApplicationWindow *)this->parent();
257         if (app && app->pasteFitResultsToPlot)
258             fitter->showLegend();
259 
260         delete fitter;
261     }
262 
263     e->accept();
264 }
265