1 /***************************************************************************
2     File                 : MultiPeakFitTool.cpp
3     Project              : QtiPlot
4     --------------------------------------------------------------------
5     Copyright            : (C) 2006,2007 by Ion Vasilief, Knut Franke
6     Email (use @ for *)  : ion_vasilief*yahoo.fr, knut.franke*gmx.de
7     Description          : Plot tool for doing multi-peak fitting.
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 "MultiPeakFitTool.h"
30 #include "RangeSelectorTool.h"
31 #include <ApplicationWindow.h>
32 #include "DataPickerTool.h"
33 
34 #include <qwt_plot_curve.h>
35 #include <qwt_plot_marker.h>
36 #include <QApplication>
37 
38 #include <gsl/gsl_statistics.h>
39 
MultiPeakFitTool(Graph * graph,ApplicationWindow * app,MultiPeakFit::PeakProfile profile,int num_peaks,const QObject * status_target,const char * status_slot)40 MultiPeakFitTool::MultiPeakFitTool(Graph *graph, ApplicationWindow *app, MultiPeakFit::PeakProfile profile, int num_peaks, const QObject *status_target, const char *status_slot)
41 	: PlotToolInterface(graph)
42 {
43 	d_selected_peaks = 0;
44 	d_curve = 0;
45 
46 	d_fit = new MultiPeakFit(app, graph, profile, num_peaks);
47 	d_fit->enablePeakCurves(app->generatePeakCurves);
48 	d_fit->setPeakCurvesColor(app->peakCurvesColor);
49 	d_fit->generateFunction(app->generateUniformFitPoints, app->fitPoints);
50 
51 	if (status_target)
52 		connect(this, SIGNAL(statusText(const QString&)), status_target, status_slot);
53 	d_picker_tool = new DataPickerTool(d_graph, app, DataPickerTool::Display, this, SIGNAL(statusText(const QString&)));
54 	d_graph->canvas()->setCursor(QCursor(QPixmap(":/cursor.png"), -1, -1));
55 
56 	QString msg = tr("Move cursor and click to select a point and double-click/press 'Enter' to set the position of a peak!");
57 	if (app->d_multi_peak_messages)
58 		QMessageBox::information(app, app->objectName(), msg);
59 	emit statusText(msg);
60 
61 	connect(d_picker_tool, SIGNAL(selected(QwtPlotCurve*,int)), this, SLOT(selectPeak(QwtPlotCurve*,int)));
62 	d_graph->canvas()->grabMouse();
63 }
64 
~MultiPeakFitTool()65 MultiPeakFitTool::~MultiPeakFitTool()
66 {
67 	d_graph->canvas()->releaseMouse();
68 
69 	foreach(QwtPlotMarker *m, d_lines)
70 		m->detach();//remove peak line markers
71 	d_lines.clear();
72 
73 	if (d_picker_tool)
74 		delete d_picker_tool;
75 	if (d_fit)
76 		delete d_fit;
77 }
78 
selectPeak(QwtPlotCurve * curve,int point_index)79 void MultiPeakFitTool::selectPeak(QwtPlotCurve *curve, int point_index)
80 {
81 	if (!curve || (d_curve && d_curve != curve))
82 		return;
83 	d_curve = curve;
84 
85 	QwtPlotMarker *m = new QwtPlotMarker();
86 	m->setXAxis(curve->xAxis());
87 	m->setLinePen(QPen(Qt::green, 2, Qt::DashLine));
88 
89 	if (curve->curveType() == QwtPlotCurve::Xfy){
90 		m->setLineStyle(QwtPlotMarker::HLine);
91 		d_fit->setInitialGuess(3*d_selected_peaks, curve->x(point_index));
92 		d_fit->setInitialGuess(3*d_selected_peaks+1, curve->y(point_index));
93 	} else {
94 		m->setLineStyle(QwtPlotMarker::VLine);
95 		d_fit->setInitialGuess(3*d_selected_peaks, curve->y(point_index));
96 		d_fit->setInitialGuess(3*d_selected_peaks+1, curve->x(point_index));
97 	}
98 
99 	m->setValue(curve->x(point_index), curve->y(point_index));
100 	d_graph->insertMarker(m);
101 	d_lines.append(m);
102 	d_graph->replot();
103 
104 	d_selected_peaks++;
105 	if (d_selected_peaks == d_fit->peaks())
106 		finalize();
107 	else {
108 		QString msg = tr("Peak %1 selected! Click to select a point and double-click/press 'Enter' to set the position of the next peak!").arg(QString::number(d_selected_peaks));
109 		ApplicationWindow *app = d_picker_tool->applicationWindow();
110 		if (app && app->d_multi_peak_messages){
111 			d_graph->canvas()->releaseMouse();
112 			QMessageBox::information(app, app->objectName(), msg);
113 			d_graph->canvas()->grabMouse();
114 		}
115 		emit statusText(msg);
116 	}
117 }
118 
finalize()119 void MultiPeakFitTool::finalize()
120 {
121 	delete d_picker_tool; d_picker_tool = NULL;
122 	d_graph->canvas()->releaseMouse();
123 
124 	if (d_fit->setDataFromCurve(d_curve->title().text())){
125 		QApplication::setOverrideCursor(Qt::WaitCursor);
126 
127 		double *y = d_fit->y();
128 		int n = d_fit->dataSize();
129 
130 		size_t imin, imax;
131 		gsl_stats_minmax_index(&imin, &imax, y, 1, n);
132 #ifdef Q_CC_MSVC
133 		QVarLengthArray<double> temp(n);
134 #else
135 		double temp[n];
136 #endif
137 		for (int i = 0; i < n; i++)
138 			temp[i] = fabs(y[i]);
139 #ifdef Q_CC_MSVC
140 		size_t imax_temp = gsl_stats_max_index(temp.data(), 1, n);
141 #else
142 		size_t imax_temp = gsl_stats_max_index(temp, 1, n);
143 #endif
144         double offset = 0.0;
145 		if (imax_temp == imax)
146 			offset = y[imin];
147 		else
148             offset = y[imax];
149         d_fit->setInitialGuess(3*d_selected_peaks, offset);
150 
151 		double w = 2*gsl_stats_sd(d_fit->x(), 1, n)/(double)d_selected_peaks;
152 		for (int i = 0; i < d_selected_peaks; i++){
153 		    int aux = 3*i;
154 			d_fit->setInitialGuess(aux + 2, w);
155 			double yc = d_fit->initialGuess(aux);
156 			if (d_fit->profile() == MultiPeakFit::Lorentz)
157                 d_fit->setInitialGuess(aux, (yc - offset)*M_PI_2*w);
158             else
159                 d_fit->setInitialGuess(aux, (yc - offset)*sqrt(M_PI_2)*w);
160 		}
161 
162 		d_fit->fit();
163 		delete d_fit; d_fit = NULL;
164 		QApplication::restoreOverrideCursor();
165 	}
166 
167 	//remove peak line markers
168 	foreach(QwtPlotMarker *m, d_lines)
169 		m->detach();
170 	d_lines.clear();
171 
172 	d_graph->replot();
173     if (d_graph->activeTool() && d_graph->activeTool()->rtti() == PlotToolInterface::Rtti_RangeSelector){
174         ((RangeSelectorTool *)d_graph->activeTool())->setEnabled();
175     } else
176         d_graph->canvas()->unsetCursor();
177 
178 	d_graph->setActiveTool(NULL);
179 }
180