1 #include "ScatterCtrl.h"
2
3 namespace Upp {
4
DoProcessing()5 void ScatterCtrl::DoProcessing()
6 {
7 ProcessingDlg dlg;
8 dlg.Init(*this);
9 dlg.Run();
10 }
11
Init(ScatterCtrl & scatter)12 void PropertiesDlg::Init(ScatterCtrl& scatter)
13 {
14 CtrlLayout(*this, t_("Scatter properties"));
15 Sizeable().Zoomable();
16
17 this->pscatter = &scatter;
18
19 tab.Add(measures, t_("Measures"));
20 tab.Add(texts, t_("Texts"));
21 tab.Add(legend, t_("Legend"));
22 tab.Add(series, t_("Series"));
23 tab.Add(general, t_("General"));
24 OnTab();
25
26 tab.WhenAction = [=]{OnTab();};
27 butOK.WhenAction = [=] {Close();};
28 }
29
Set(int itab)30 void PropertiesDlg::Set(int itab)
31 {
32 tab.Set(itab);
33 OnTab();
34 }
35
OnTab()36 void PropertiesDlg::OnTab()
37 {
38 if (tab.IsAt(measures))
39 measures.Init(*pscatter);
40 else if (tab.IsAt(texts))
41 texts.Init(*pscatter);
42 else if (tab.IsAt(legend))
43 legend.Init(*pscatter);
44 else if (tab.IsAt(series))
45 series.Init(*pscatter);
46 else if (tab.IsAt(general))
47 general.Init(*pscatter);
48 }
49
Init(ScatterCtrl & scatter)50 void ProcessingDlg::Init(ScatterCtrl& scatter)
51 {
52 Title(Nvl(scatter.GetTitle(), "Data") + " processing");
53
54 Add(splitter.SizePos());
55 CtrlLayout(list);
56 CtrlLayout(right);
57 splitter.Horz(list.SizePos(), right.SizePos());
58 splitter.SetPos(1500, 0);
59 Sizeable().Zoomable();
60
61 this->pscatter = &scatter;
62
63 list.list.Reset();
64 list.list.SetLineCy(EditField::GetStdHeight());
65 list.list.AddColumn(t_("Name"));
66 list.list.AddColumn(t_("Id"));
67 list.list.ColumnWidths("1 0");
68 for(int i = 0; i < scatter.GetCount(); i++) {
69 if (scatter.ScatterDraw::IsVisible(i)) {
70 list.list.Add(scatter.GetLegend(i), i);
71 ProcessingTab& tab = tabs.Add();
72 tab.Init(scatter);
73 CtrlLayout(tab);
74 right.rect.Add(tab.SizePos());
75 }
76 }
77 if (list.list.GetCount() > 0)
78 list.list.SetCursor(0);
79 list.list.WhenSel = [=] {UpdateFields();};
80 UpdateFields();
81
82 right.butOK.WhenAction = [=] {Close();};
83 }
84
UpdateFields()85 void ProcessingDlg::UpdateFields()
86 {
87 int index = list.list.GetCursor();
88 if (index < 0)
89 return;
90
91 for (int i = 0; i < list.list.GetCount(); ++i)
92 tabs[i].Hide();
93 tabs[index].UpdateField(~list.list.Get(0), int(list.list.Get(1)));
94 }
95
r2Compare(const Vector<Value> & v1,const Vector<Value> & v2)96 int r2Compare(const Vector<Value>& v1, const Vector<Value>& v2) {return double(v1[2]) > double(v2[2]);}
97
ProcessingTab()98 ProcessingTab::ProcessingTab()
99 {
100 CtrlLayout(*this);
101
102 CtrlLayout(tabFitLeft);
103 CtrlLayout(tabFitRight);
104
105 splitterTabFit.Horz(tabFitLeft.SizePos(), tabFitRightScroll.AddPaneV(tabFitRight).SizePos());
106 splitterTabFit.SetPos(7000, 0);
107 CtrlLayout(tabFreqLeft);
108 CtrlLayout(tabFreqRight);
109 splitterTabFreq.Horz(tabFreqLeft.SizePos(), tabFreqRightScroll.AddPaneV(tabFreqRight).SizePos());
110 splitterTabFreq.SetPos(8000, 0);
111 CtrlLayout(tabOpLeft);
112 CtrlLayout(tabOpRight);
113 splitterTabOp.Horz(tabOpLeft.SizePos(), tabOpRight.SizePos());
114 splitterTabOp.SetPos(8500, 0);
115 CtrlLayout(tabBestFitLeft);
116 CtrlLayout(tabBestFitRight);
117 splitterTabBestFit.Horz(tabBestFitLeft.SizePos(), tabBestFitRight.SizePos());
118 splitterTabBestFit.SetPos(6000, 0);
119 CtrlLayout(tabHistLeft);
120 CtrlLayout(tabHistRight);
121 splitterTabHist.Horz(tabHistLeft.SizePos(), tabHistRight.SizePos());
122 splitterTabHist.SetPos(8000, 0);
123
124 tab.Add(splitterTabFit.SizePos(), t_("Processing"));
125 tab.Add(splitterTabFreq.SizePos(), t_("Frequency"));
126 tab.Add(splitterTabOp.SizePos(), t_("Operations"));
127 tab.Add(splitterTabBestFit.SizePos(), t_("Best fit"));
128 tab.Add(splitterTabHist.SizePos(), t_("Histogram"));
129 tab.WhenSet = [=] {OnSet();};
130
131 tabFreqRight.butFFT.WhenAction = [=] {OnFFT();};
132 tabFreqRight.opXAxis = 0;
133 tabFreqRight.opXAxis.WhenAction = [=] {OnFFT();};
134 tabFreqRight.type.WhenAction = [=] {OnFFT();};
135 tabFreqRight.type = 0;
136
137 for (int i = 0; i < DataSource::GetFFTWindowCount(); ++i)
138 tabFreqRight.window.Add(InitCaps(DataSource::GetFFTWindowStr(i)));
139 tabFreqRight.window.SetIndex(1);
140 tabFreqRight.window.WhenAction = [=] {OnFFT();};
141 tabFreqRight.num <<= 1;
142 tabFreqRight.overlapping <<= 0.1;
143
144 tabFitRight.opSeries = true;
145 tabFitRight.opSeries.WhenAction = [=] {OnOp();};
146 tabFitRight.opAverage.WhenAction = [=] {OnOp();};
147 tabFitRight.opLinear.WhenAction = [=] {OnOp();};
148 tabFitRight.opCuadratic.WhenAction = [=] {OnOp();};
149 tabFitRight.opCubic.WhenAction = [=] {OnOp();};
150 tabFitRight.opSinus.WhenAction = [=] {OnOp();};
151 tabFitRight.opSinusTend.WhenAction = [=] {OnOp();};
152 tabFitRight.opSpline.WhenAction = [=] {OnOp();};
153 tabFitRight.opDerivative.WhenAction = [=] {OnOp();};
154 tabFitRight.derOrder.WhenAction = [=] {OnOp();};
155 tabFitRight.derAccuracy.WhenAction = [=] {OnOp();};
156 tabFitRight.opSG.WhenAction = [=] {OnOp();};
157 tabFitRight.sgOrder.WhenAction = [=] {OnOp();};
158 tabFitRight.sgSize.WhenAction = [=] {OnOp();};
159 tabFitRight.sgDeg.WhenAction = [=] {OnOp();};
160 tabFitRight.opMax.WhenAction = [=] {OnOp();};
161 tabFitRight.opMin.WhenAction = [=] {OnOp();};
162 tabFitRight.opMovAvg.WhenAction = [=] {OnOp();};
163 tabFitRight.opSecAvg.WhenAction = [=] {OnOp();};
164 tabFitRight.opCumAvg.WhenAction = [=] {OnOp();};
165 tabFitRight.butAutoSensSector.WhenAction = [=] {OnAutoSensSector();};
166 tabFitRight.width.WhenLostFocus = [=] {OnUpdateSensitivity();};
167 tabFitRight.width.WhenAction = [=] {OnUpdateSensitivity();};
168
169 tabFitRight.opDerivative.Tip(t_("Numerical derivative including derivative order and accuracy (related to window size)"));
170 tabFitRight.derOrder <<= 1;
171 tabFitRight.derOrder.Tip(t_("Implemented orders are 1 (first) and 2 (second derivative)"));
172 tabFitRight.derAccuracy <<= 6;
173 tabFitRight.derAccuracy.SetInc(2);
174 tabFitRight.derAccuracy.Tip(t_("Implemented accuracies are 2, 4, 6 and 8"));
175 tabFitRight.opSG.Tip(t_("Savitzky–Golay filter including derivative order, window size and polynomial degree"));
176 tabFitRight.sgOrder <<= 0;
177 tabFitRight.sgOrder.Tip(t_("Implemented orders are 0 (just filter), 1 (first) and 2 (second derivative)"));
178 tabFitRight.sgSize <<= 5;
179 //tabFitRight.sgSize.SetInc(2);
180 tabFitRight.sgSize.Tip(t_("Window size"));
181 tabFitRight.sgDeg <<= 3;
182 tabFitRight.sgDeg.Tip(t_("Polynomial degree"));
183 tabFitRight.numDecimals <<= 3;
184 tabFitRight.numDecimals.WhenAction = [=] {UpdateEquations();};
185 tabFitRight.showEquation.WhenAction = [=] {OnShowEquation();};
186
187 tabOpRight.xLow.WhenLostFocus = [=] {OnOperation();};
188 tabOpRight.xHigh.WhenLostFocus = [=] {OnOperation();};
189
190 tabBestFitRight.coefficients = 0;
191 tabBestFitRight.coefficients.Tip(t_("To show real equation coefficients with different precisions or just in text"));
192 tabBestFitRight.minR2 = 0.6;
193 tabBestFitRight.minR2.Tip(t_("Min. R2 to plot the equation"));
194 tabBestFitRight.userFormula <<= "c0 + c1*x^2; c0=0; c1=1";
195 tabBestFitRight.userFormula.Tip(t_("User suppled equation. Initial guess values separated with ';'"));
196 tabBestFitRight.gridTrend.AddColumn(t_("Type"), 10);
197 tabBestFitRight.gridTrend.AddColumn(t_("Equation"), 30);
198 tabBestFitRight.gridTrend.AddColumn(t_("R2"), 5);
199 tabBestFitRight.gridTrend.SetLineCy(EditField::GetStdHeight()).MultiSelect();
200 tabBestFitRight.gridTrend.WhenBar = [=](Bar &menu) {OnArrayBar(menu);};
201 tabBestFitRight.gridTrend.Sort(r2Compare);
202 for (int i = 0; i < ExplicitEquation::GetEquationCount(); ++i)
203 equationTypes.Add(ExplicitEquation::Create(i));
204 userEquation = new UserEquation;
205 equationTypes.Add(userEquation);
206
207 tabBestFitRight.butFit.Tip(t_("It tries to fit the series with the supported equations"));
208 tabBestFitRight.butFit.WhenPush = [=] {OnFit();};
209
210 tabHistRight.axis.Add(t_("X"));
211 tabHistRight.axis.Add(t_("Y"));
212 tabHistRight.axis.SetIndex(1);
213 tabHistRight.axis.WhenAction = [=] {OnSet();};
214 tabHistRight.butHist.WhenAction = [=] {OnHist();};
215 tabHistRight.numVals <<= 30;
216 tabHistRight.valNormalize <<= 100;
217 tabHistRight.opStaggered <<= true;
218
219 tabHistRight.opNormalize.WhenAction = [&] {
220 tabHistRight.valNormalize.Enable(~tabHistRight.opNormalize);
221 tabHistRight.labNormalize.Enable(~tabHistRight.opNormalize);
222 };
223 tabHistRight.opNormalize.WhenAction();
224
225 tabFreqFirst = tabOpFirst = tabBestFitFirst = tabHistFirst = true;
226 avgFirst = linearFirst = cuadraticFirst = cubicFirst = sinusFirst = sinusTendFirst = splineFirst = true;
227
228 exclamationOpened = false;
229 newWidthMax = newWidthMin = newWidthMovAvg-1;
230 mpm = Null;
231 }
232
ArrayCopy()233 void ProcessingTab::ArrayCopy() {
234 tabBestFitRight.gridTrend.SetClipboard(true, true);
235 }
236
ArraySelect()237 void ProcessingTab::ArraySelect() {
238 tabBestFitRight.gridTrend.Select(0, tabBestFitRight.gridTrend.GetCount(), true);
239 }
240
OnArrayBar(Bar & menu)241 void ProcessingTab::OnArrayBar(Bar &menu) {
242 menu.Add(t_("Select all"), Null, [=] {ArraySelect();}).Key(K_CTRL_A).Help(t_("Select all rows"));
243 menu.Add(t_("Copy"), ScatterImgP::Copy(), [=] {ArrayCopy();}).Key(K_CTRL_C).Help(t_("Copy selected rows"));
244 }
245
OnFit()246 void ProcessingTab::OnFit() {
247 WaitCursor waitcursor;
248
249 if (pscatter->IsDeletedDataSource(id))
250 return;
251 DataSource &ds = pscatter->GetDataSource(id);
252
253 userEquation->Init("User", ~tabBestFitRight.userFormula, "x");
254
255 Array<double> r2;
256 r2.SetCount(equationTypes.GetCount());
257
258 for (int i = 0; i < equationTypes.GetCount(); ++i) {
259 equationTypes[i].GuessCoeff(ds);
260 equationTypes[i].Fit(ds, r2[i]);
261 }
262 tabBestFitLeft.scatter.RemoveAllSeries();
263 tabBestFitLeft.scatter.AddSeries(ds).Legend("Series").NoMark();
264 for (int i = 0; i < equationTypes.GetCount(); ++i) {
265 if (r2[i] >= tabBestFitRight.minR2)
266 tabBestFitLeft.scatter.AddSeries(equationTypes[i]).Legend(equationTypes[i].GetFullName()).NoMark().Stroke(2);
267 }
268 tabBestFitLeft.scatter.ZoomToFit(true, true);
269
270 int numDecimals = 3;
271 switch (tabBestFitRight.coefficients) {
272 case 1: numDecimals = 40; break;
273 case 2: numDecimals = Null; break;
274 }
275 tabBestFitRight.gridTrend.Clear();
276 for (int i = 0; i < equationTypes.GetCount(); ++i)
277 tabBestFitRight.gridTrend.Add(equationTypes[i].GetFullName(), equationTypes[i].GetEquation(numDecimals), r2[i]);
278 tabBestFitRight.gridTrend.SetSortColumn(2, true);
279 }
280
OnOp()281 void ProcessingTab::OnOp()
282 {
283 if (tabFitLeft.scatter.IsDeletedDataSource(0))
284 return;
285 DataSource &data = tabFitLeft.scatter.GetDataSource(0);
286
287 if (data.IsParam() || data.IsExplicit())
288 return;
289
290 if (~tabFitRight.opAverage && avgFirst) {
291 double r2;
292 average.Fit(data, r2);
293 avgFirst = false;
294 }
295 if (~tabFitRight.opLinear && linearFirst) {
296 if (linear.Fit(data, r2Linear) < 0) {
297 tabFitRight.opLinear <<= false;
298 tabFitRight.opLinear.Enable(false);
299 } else
300 linearFirst = false;
301 }
302 if (~tabFitRight.opCuadratic && cuadraticFirst) {
303 cuadratic.GuessCoeff(data);
304 if (cuadratic.Fit(data, r2Cuadratic) < 0) {
305 tabFitRight.opCuadratic <<= false;
306 tabFitRight.opCuadratic.Enable(false);
307 } else
308 cuadraticFirst = false;
309 }
310 if (~tabFitRight.opCubic && cubicFirst) {
311 cubic.GuessCoeff(data);
312 if (cubic.Fit(data, r2Cubic) < 0) {
313 tabFitRight.opCubic <<= false;
314 tabFitRight.opCubic.Enable(false);
315 } else
316 cubicFirst = false;
317 }
318 if (~tabFitRight.opSinus && sinusFirst) {
319 sinus.GuessCoeff(data);
320 if (sinus.Fit(data, r2Sinus) < 0) {
321 tabFitRight.opSinus <<= false;
322 tabFitRight.opSinus.Enable(false);
323 } else
324 sinusFirst = false;
325 }
326 if (~tabFitRight.opSinusTend && sinusTendFirst) {
327 DataXRange dataXRange;
328 dataXRange.Init(data, Null, Null);
329 double r2SinusTendBest = Null;
330 SinEquation sinusTendBest;
331 for (int iLow = 9; iLow >= 0; iLow--) {
332 double xLow = data.x(int64(data.GetCount()*iLow/10.));
333 dataXRange.SetXLow(xLow);
334 sinusTend.GuessCoeff(dataXRange);
335 if (sinusTend.Fit(dataXRange, r2SinusTend) < 0)
336 break;
337 if (!IsNull(r2SinusTendBest) && r2SinusTendBest > r2SinusTend)
338 break;
339 r2SinusTendBest = r2SinusTend;
340 sinusTendBest = sinusTend;
341 }
342 if (IsNull(r2SinusTendBest)) {
343 tabFitRight.opSinusTend <<= false;
344 tabFitRight.opSinusTend.Enable(false);
345 } else {
346 splineFirst = false;
347 r2SinusTend = r2SinusTendBest;
348 sinusTend = sinusTendBest;
349 }
350 }
351 if (~tabFitRight.opSpline && splineFirst) {
352 if (spline.Fit(data) < 0) {
353 tabFitRight.opSpline <<= false;
354 tabFitRight.opSpline.Enable(false);
355 } else
356 splineFirst = false;
357 }
358 OnUpdateSensitivity();
359
360 tabFitLeft.scatter.ScatterDraw::Show(0, tabFitRight.opSeries);
361 tabFitLeft.scatter.ScatterDraw::Show(1, tabFitRight.opAverage);
362 tabFitLeft.scatter.ScatterDraw::Show(2, tabFitRight.opLinear);
363 tabFitLeft.scatter.ScatterDraw::Show(3, tabFitRight.opCuadratic);
364 tabFitLeft.scatter.ScatterDraw::Show(4, tabFitRight.opCubic);
365 tabFitLeft.scatter.ScatterDraw::Show(5, tabFitRight.opSinus);
366 tabFitLeft.scatter.ScatterDraw::Show(6, tabFitRight.opSinusTend);
367 tabFitLeft.scatter.ScatterDraw::Show(7, tabFitRight.opSpline);
368 tabFitLeft.scatter.ScatterDraw::Show(8, tabFitRight.opDerivative);
369 tabFitLeft.scatter.ScatterDraw::Show(9, tabFitRight.opSG);
370 tabFitLeft.scatter.ScatterDraw::Show(10,tabFitRight.opMax);
371 tabFitLeft.scatter.ScatterDraw::Show(11,tabFitRight.opMin);
372 tabFitLeft.scatter.ScatterDraw::Show(12,tabFitRight.opMovAvg);
373 tabFitLeft.scatter.ScatterDraw::Show(13,tabFitRight.opSecAvg);
374 tabFitLeft.scatter.ScatterDraw::Show(14,tabFitRight.opCumAvg);
375
376 UpdateEquations();
377 OnShowEquation();
378 }
379
OnAutoSensSector()380 void ProcessingTab::OnAutoSensSector()
381 {
382 if (tabFitLeft.scatter.IsDeletedDataSource(0))
383 return;
384 DataSource &data = tabFitLeft.scatter.GetDataSource(0);
385 Vector<Pointf> secAvg;
386 double baseWidth;
387
388 baseWidth = 0;
389 for (int64 i = 1; i < data.GetCount(); ++i)
390 baseWidth += (data.x(i) - data.x(i-1));
391 baseWidth /= (data.GetCount() - 1);
392
393 double rangeX = data.x(data.GetCount() - 1) - data.x(int64(0));
394
395 for(double width = baseWidth; width < rangeX/10.; width += baseWidth) {
396 secAvg = data.SectorAverageY(width);
397 VectorPointf sector(secAvg);
398 Vector<int64> ids;
399 sector.MaxListY(ids, 10*baseWidth);
400 if (ids.GetCount() < 5) {
401 tabFitRight.width <<= width;
402 return;
403 }
404 }
405 tabFitLeft.scatter.Refresh();
406 }
407
OnOperation()408 void ProcessingTab::OnOperation()
409 {
410 if (exclamationOpened) // To avoid WhenLostFocus to be called when Exclamation() is opened
411 return;
412 exclamationOpened = true;
413 if (!IsNull(tabOpRight.xLow) && !IsNull(tabOpRight.xHigh)) {
414 if (tabOpRight.xLow >= tabOpRight.xHigh) {
415 Exclamation(t_("'x >' has to be lower than 'x <'"));
416 exclamationOpened = false;
417 return;
418 }
419 }
420 exclamationOpened = false;
421 if (pscatter->IsDeletedDataSource(id))
422 return;
423 dataXRange.Init(pscatter->GetDataSource(id), tabOpRight.xLow, tabOpRight.xHigh);
424 tabOpLeft.scatter.Refresh();
425 }
426
UpdateField(const String _name,int _id)427 void ProcessingTab::UpdateField(const String _name, int _id)
428 {
429 id = _id;
430 name.SetText(_name);
431
432 tabFitLeft.scatter.RemoveAllSeries();
433 if (pscatter->IsDeletedDataSource(id))
434 return;
435 tabFitLeft.scatter.AddSeries(pscatter->GetDataSource(id)).SetSequentialX(pscatter->GetSequentialX())
436 .Legend(pscatter->GetLegend(id));
437 tabFitLeft.scatter.SetFastViewX(pscatter->GetFastViewX());
438
439 tabFitLeft.scatter.SetFillColor(0, pscatter->GetFillColor(id));
440 tabFitLeft.scatter.Dash(0, pscatter->GetDash(id));
441
442 Upp::Color color;
443 double thickness;
444 pscatter->GetStroke(0, thickness, color);
445 tabFitLeft.scatter.Stroke(0, thickness, color);
446 tabFitLeft.scatter.MarkStyle(0, pscatter->GetMarkStyleName(id));
447 tabFitLeft.scatter.SetMarkColor(0, pscatter->GetMarkColor(id));
448 tabFitLeft.scatter.SetMarkWidth(0, pscatter->GetMarkWidth(id));
449 tabFitLeft.scatter.MarkStyle(0, pscatter->GetMarkStyleName(id));
450 tabFitLeft.scatter.SetLegendAnchor(ScatterDraw::RIGHT_TOP).SetLegendFillColor(Null);
451
452 tabFitLeft.scatter.Units(0, pscatter->GetUnitsY(id), pscatter->GetUnitsX(id));
453 tabFitLeft.scatter.SetLabelX(pscatter->GetLabelX());
454
455 bool primary = pscatter->IsDataPrimaryY(id);
456 tabFitLeft.scatter.SetRange(pscatter->GetXRange(), primary ? pscatter->GetYRange() : pscatter->GetY2Range());
457 tabFitLeft.scatter.SetMajorUnits(pscatter->GetMajorUnitsX(), primary ? pscatter->GetMajorUnitsY() : pscatter->GetMajorUnitsY2());
458 tabFitLeft.scatter.SetXYMin(pscatter->GetXMin(), primary ? pscatter->GetYMin() : pscatter->GetY2Min());
459
460 tabFitLeft.scatter.ShowInfo().ShowContextMenu().ShowProcessDlg().ShowPropertiesDlg().SetMouseHandlingLinked(true, true);
461
462 if (tabFitLeft.scatter.IsDeletedDataSource(0))
463 return;
464 DataSource &data = tabFitLeft.scatter.GetDataSource(0);
465
466 if (!data.IsParam()/* && !data.IsExplicit()*/) {
467 double avg = data.AvgY();
468 tabFitRight.eAverage = avg;
469 tabFitRight.eRMS = data.RMSY();
470 tabFitRight.eStdDev = data.StdDevY(avg);
471 int64 idmx;
472 double val;
473 val = data.MaxY(idmx);
474 if (!IsNull(val)) {
475 tabFitRight.eMax <<= Format("(%f,%f)", data.x(idmx), val);
476 Pointf p = data.MaxSubDataImpY(idmx, 3);
477 if (!IsNull(p))
478 tabFitRight.eMaxImp = Format("(%f,%f)", p.x, p.y);
479 val = data.MinY(idmx);
480 if (!IsNull(val))
481 tabFitRight.eMin = Format("(%f,%f)", data.x(idmx), val);
482 }
483 }
484 if (!data.IsParam() && !data.IsExplicit()) {
485 tabFitRight.width <<= pscatter->GetXRange()/15.;
486 tabFitRight.width.SetInc(pscatter->GetXRange()/15./2.);
487
488 tabFitLeft.scatter.AddSeries(average).NoMark().Stroke(1.5);
489 tabFitLeft.scatter.AddSeries(linear).NoMark().Stroke(1.5);
490 tabFitLeft.scatter.AddSeries(cuadratic).NoMark().Stroke(1.5);
491 tabFitLeft.scatter.AddSeries(cubic).NoMark().Stroke(1.5);
492 tabFitLeft.scatter.AddSeries(sinus).NoMark().Stroke(1.5);
493 tabFitLeft.scatter.AddSeries(sinusTend).NoMark().Stroke(1.5);
494 tabFitLeft.scatter.AddSeries(spline).NoMark().Dash(LINE_SOLID).Stroke(1.5);
495 tabFitLeft.scatter.AddSeries(derivative).NoMark().Dash(LINE_SOLID).Stroke(1.5);
496 tabFitLeft.scatter.AddSeries(sg).NoMark().Dash(LINE_SOLID).Stroke(1.5);
497 tabFitLeft.scatter.AddSeries(upperEnvelope).Legend(pscatter->GetLegend(id) + String("-") + t_("Max"))
498 .NoMark().Dash(LINE_DASHED).Stroke(1.5).SetSequentialX(true);
499 tabFitLeft.scatter.AddSeries(lowerEnvelope).Legend(pscatter->GetLegend(id) + String("-") + t_("Min"))
500 .NoMark().Dash(LINE_DASHED).SetSequentialX(true);
501 tabFitLeft.scatter.AddSeries(movAvg).Stroke(1.5).Legend(pscatter->GetLegend(id) + String("-") + t_("MovAvg")).NoMark();
502 tabFitLeft.scatter.AddSeries(secAvg).Stroke(1.5).Legend(pscatter->GetLegend(id) + String("-") + t_("SecAvg")).NoMark();
503 tabFitLeft.scatter.AddSeries(cumAvg).Stroke(1.5).Legend(pscatter->GetLegend(id) + String("-") + t_("CumAvg")).NoMark();
504
505 OnOp();
506 } else {
507 tabFitRight.opSeries.Enable(false);
508 tabFitRight.opAverage.Enable(false);
509 tabFitRight.opLinear.Enable(false);
510 tabFitRight.opCuadratic.Enable(false);
511 tabFitRight.opCubic.Enable(false);
512 tabFitRight.opSinus.Enable(false);
513 tabFitRight.opSinusTend.Enable(false);
514 tabFitRight.opSpline.Enable(false);
515 tabFitRight.opDerivative.Enable(false);
516 tabFitRight.derOrder.Enable(false);
517 tabFitRight.derAccuracy.Enable(false);
518 tabFitRight.opSG.Enable(false);
519 tabFitRight.sgOrder.Enable(false);
520 tabFitRight.opMax.Enable(false);
521 tabFitRight.opMin.Enable(false);
522 tabFitRight.opMovAvg.Enable(false);
523 tabFitRight.opSecAvg.Enable(false);
524 tabFitRight.opCumAvg.Enable(false);
525 }
526
527 Show();
528 }
529
OnUpdateSensitivity()530 void ProcessingTab::OnUpdateSensitivity()
531 {
532 if (tabFitLeft.scatter.IsDeletedDataSource(0))
533 return;
534 DataSource &data = tabFitLeft.scatter.GetDataSource(0);
535
536 bool refresh = false;
537 if (tabFitRight.opDerivative) {
538 bool isOdd = int(~(tabFitRight.derAccuracy))%2;
539 if (IsNull(tabFitRight.derAccuracy) || isOdd)
540 derivative.Clear();
541 else
542 derivative = data.DerivativeY(~tabFitRight.derOrder, ~tabFitRight.derAccuracy);
543
544 refresh = true;
545 }
546 if (tabFitRight.opSG) {
547 int side = int(~tabFitRight.sgSize)/2;
548 if (!SavitzkyGolay_CheckParams(side, side, ~tabFitRight.sgDeg, ~tabFitRight.sgOrder))
549 sg.Clear();
550 else
551 sg = data.SavitzkyGolayY(~tabFitRight.sgDeg, ~tabFitRight.sgSize, ~tabFitRight.sgOrder);
552
553 refresh = true;
554 }
555 if (tabFitRight.opMax && newWidthMax != tabFitRight.width) {
556 newWidthMax = tabFitRight.width;
557
558 upperEnvelope.Clear();
559 Vector<int64> idsUpper = data.UpperEnvelopeY(tabFitRight.width);
560 mpm = data.StdDevY()*sqrt(2*log(idsUpper.GetCount()));
561 for (int i = 0; i < idsUpper.GetCount(); ++i)
562 upperEnvelope << Pointf(data.x(idsUpper[i]), data.y(idsUpper[i]));
563 refresh = true;
564 }
565 tabFitRight.labNumMax.Enable(tabFitRight.opMax);
566 tabFitRight.numMax.Enable(tabFitRight.opMax);
567 tabFitRight.numMax <<= (tabFitRight.opMax ? upperEnvelope.GetCount() : Null);
568 tabFitRight.labMPM.Enable(tabFitRight.opMax);
569 tabFitRight.eMPM.Enable(tabFitRight.opMax);
570 tabFitRight.eMPM <<= (tabFitRight.opMax ? mpm : Null);
571 if (tabFitRight.opMin && newWidthMin != tabFitRight.width) {
572 newWidthMin = tabFitRight.width;
573
574 lowerEnvelope.Clear();
575 Vector<int64> idsLower = data.LowerEnvelopeY(tabFitRight.width);
576 for (int i = 0; i < idsLower.GetCount(); ++i)
577 lowerEnvelope << Pointf(data.x(idsLower[i]), data.y(idsLower[i]));
578 refresh = true;
579 }
580 tabFitRight.labNumMin.Enable(tabFitRight.opMin);
581 tabFitRight.numMin.Enable(tabFitRight.opMin);
582 tabFitRight.numMin <<= (tabFitRight.opMin ? lowerEnvelope.GetCount() : Null);
583 if (tabFitRight.opMovAvg && newWidthMovAvg != tabFitRight.width) {
584 newWidthMovAvg = tabFitRight.width;
585
586 movAvg = data.MovingAverageY(tabFitRight.width);
587 refresh = true;
588 }
589 if (tabFitRight.opSecAvg && newWidthMovAvg != tabFitRight.width) {
590 newWidthMovAvg = tabFitRight.width;
591
592 secAvg = data.SectorAverageY(tabFitRight.width);
593 refresh = true;
594 }
595 if (tabFitRight.opCumAvg) {
596 cumAvg = data.CumulativeAverageY();
597 refresh = true;
598 }
599
600 if (refresh)
601 tabFitLeft.scatter.Refresh();
602 }
603
OnSet()604 void ProcessingTab::OnSet()
605 {
606 if (tabFitLeft.scatter.IsDeletedDataSource(0))
607 return;
608 DataSource &data = tabFitLeft.scatter.GetDataSource(0);
609
610 if (tabFreqFirst && tab.IsAt(splitterTabFreq)) {
611 tabFreqFirst = false;
612 if (data.IsParam() || data.IsExplicit()) {
613 tabFreqLeft.comments.SetText(t_("Impossible to calculate FFT from a not sampled series"));
614 tabFreqRight.butFFT.Enable(false);
615 } else if (data.GetCount() < 5) {
616 tabFreqLeft.comments.SetText(t_("Insufficient data to calculate FFT"));
617 tabFreqRight.butFFT.Enable(false);
618 } else {
619 double mindT, maxdT = Null;
620 mindT = -maxdT;
621 for (int64 i = 1; i < data.GetCount(); ++i) {
622 double d2 = data.x(i), d1 = data.x(i - 1);
623
624 if (!IsNull(d1) && !IsNull(d2)) {
625 double delta = (d2 - d1);
626 mindT = min(delta, mindT);
627 maxdT = max(delta, maxdT);
628 }
629 }
630 if ((maxdT - mindT)/maxdT > 0.00001)
631 tabFreqLeft.comments.SetText(Format(t_("Sampling time changes from %f to %f"), mindT, maxdT));
632 else
633 tabFreqLeft.comments.SetText("");
634 tabFreqRight.samplingTime = (maxdT + mindT)/2.;
635 }
636 } else if (tabOpFirst && tab.IsAt(splitterTabOp)) {
637 if (pscatter->IsDeletedDataSource(id))
638 return;
639
640 tabOpFirst = false;
641
642 tabOpLeft.scatter.RemoveAllSeries();
643 String legend = pscatter->GetLegend(id);
644 double xLow = pscatter->GetDataSource(id).MinX();
645 if (IsNull(xLow))
646 xLow = pscatter->GetXMin();
647 tabOpRight.xLow <<= xLow;
648 double xHigh = pscatter->GetDataSource(id).MaxX();
649 if (IsNull(xHigh))
650 xHigh = pscatter->GetXMin() + pscatter->GetXRange();
651 tabOpRight.xHigh <<= xHigh;
652 dataXRange.Init(pscatter->GetDataSource(id), xLow, xHigh);
653 tabOpLeft.scatter.AddSeries(dataXRange).SetSequentialX(pscatter->GetSequentialX())
654 .Legend(legend + String("-") + t_("Processed")).NoMark()
655 .Stroke(8, Upp::Color(115, 214, 74));
656 tabOpLeft.scatter.AddSeries(pscatter->GetDataSource(id)).SetSequentialX(pscatter->GetSequentialX())
657 .Legend(legend).NoMark().Stroke(2, Blue());
658 tabOpLeft.scatter.SetFastViewX(pscatter->GetFastViewX());
659
660 tabOpLeft.scatter.SetLegendAnchor(ScatterDraw::RIGHT_TOP).SetLegendFillColor(Null);
661
662 tabOpLeft.scatter.Units(0, pscatter->GetUnitsY(id), pscatter->GetUnitsX(id));
663 tabOpLeft.scatter.SetLabelX(pscatter->GetLabelX());
664
665 bool primary = pscatter->IsDataPrimaryY(id);
666 tabOpLeft.scatter.SetRange(pscatter->GetXRange(), primary ? pscatter->GetYRange() : pscatter->GetY2Range());
667 tabOpLeft.scatter.SetMajorUnits(pscatter->GetMajorUnitsX(), primary ? pscatter->GetMajorUnitsY() : pscatter->GetMajorUnitsY2());
668 tabOpLeft.scatter.SetXYMin(pscatter->GetXMin(), primary ? pscatter->GetYMin() : pscatter->GetY2Min());
669
670 tabOpLeft.scatter.ShowInfo().ShowContextMenu().ShowProcessDlg().ShowPropertiesDlg().SetMouseHandlingLinked(true, true);
671 } else if (tabBestFitFirst && tab.IsAt(splitterTabBestFit)) {
672 if (pscatter->IsDeletedDataSource(id))
673 return;
674
675 tabBestFitFirst = false;
676
677 tabBestFitLeft.scatter.RemoveAllSeries();
678 String legend = pscatter->GetLegend(id);
679
680 tabBestFitLeft.scatter.AddSeries(pscatter->GetDataSource(id)).SetSequentialX(pscatter->GetSequentialX())
681 .Legend(legend).NoMark().Stroke(2);
682 tabBestFitLeft.scatter.SetFastViewX(pscatter->GetFastViewX());
683
684 tabBestFitLeft.scatter.SetLegendAnchor(ScatterDraw::RIGHT_TOP).SetLegendFillColor(Null);
685
686 tabBestFitLeft.scatter.Units(0, pscatter->GetUnitsY(id), pscatter->GetUnitsX(id));
687 tabBestFitLeft.scatter.SetLabelX(pscatter->GetLabelX());
688
689 bool primary = pscatter->IsDataPrimaryY(id);
690 tabBestFitLeft.scatter.SetRange(pscatter->GetXRange(), primary ? pscatter->GetYRange() : pscatter->GetY2Range());
691 tabBestFitLeft.scatter.SetMajorUnits(pscatter->GetMajorUnitsX(), primary ? pscatter->GetMajorUnitsY() : pscatter->GetMajorUnitsY2());
692 tabBestFitLeft.scatter.SetXYMin(pscatter->GetXMin(), primary ? pscatter->GetYMin() : pscatter->GetY2Min());
693
694 tabBestFitLeft.scatter.ShowInfo().ShowContextMenu().ShowProcessDlg().ShowPropertiesDlg().SetMouseHandlingLinked(true, true);
695 } else if (tab.IsAt(splitterTabHist)) {
696 //tabHistFirst = false;
697 if (data.IsParam() || data.IsExplicit()) {
698 tabHistLeft.comments.SetText(t_("Impossible to calculate histogram from a not sampled series"));
699 tabHistRight.butHist.Enable(false);
700 } else if (data.GetCount() < 5) {
701 tabHistLeft.comments.SetText(t_("Insufficient data to calculate histogram"));
702 tabHistRight.butHist.Enable(false);
703 } else {
704 if (~tabHistRight.axis == t_("Y")) {
705 tabHistRight.minVal <<= data.MinY();
706 tabHistRight.maxVal <<= data.MaxY();
707 } else {
708 tabHistRight.minVal <<= data.MinX();
709 tabHistRight.maxVal <<= data.MaxX();
710 }
711 }
712 }
713 }
714
UpdateEquations()715 void ProcessingTab::UpdateEquations()
716 {
717 tabFitRight.eqAverage = tabFitRight.opAverage ? average.GetEquation(tabFitRight.numDecimals) : "";
718 tabFitRight.eqLinear = tabFitRight.opLinear ? linear.GetEquation(tabFitRight.numDecimals) : "";
719 tabFitRight.r2Linear = tabFitRight.opLinear ? r2Linear : Null;
720 tabFitRight.eqCuadratic = tabFitRight.opCuadratic ? cuadratic.GetEquation(tabFitRight.numDecimals) : "";
721 tabFitRight.r2Cuadratic = tabFitRight.opCuadratic ? r2Cuadratic : Null;
722 tabFitRight.eqCubic = tabFitRight.opCubic ? cubic.GetEquation(tabFitRight.numDecimals) : "";
723 tabFitRight.r2Cubic = tabFitRight.opCubic ? r2Cubic : Null;
724 tabFitRight.eqSinus = tabFitRight.opSinus ? sinus.GetEquation(tabFitRight.numDecimals) : "";
725 tabFitRight.r2Sinus = tabFitRight.opSinus ? r2Sinus : Null;
726 tabFitRight.eqSinusTend = tabFitRight.opSinusTend ? sinusTend.GetEquation(tabFitRight.numDecimals) : "";
727 tabFitRight.r2SinusTend = tabFitRight.opSinusTend ? r2SinusTend : Null;
728 }
729
OnShowEquation()730 void ProcessingTab::OnShowEquation()
731 {
732 bool show = tabFitRight.showEquation;
733 tabFitLeft.scatter.Legend(1, pscatter->GetLegend(id) + String("-") +
734 (show && tabFitRight.opAverage ? average.GetEquation(tabFitRight.numDecimals) : String(t_("Average"))));
735 tabFitLeft.scatter.Legend(2, pscatter->GetLegend(id) + String("-") +
736 (show && tabFitRight.opLinear ? linear.GetEquation(tabFitRight.numDecimals) : String(t_("Linear"))));
737 tabFitLeft.scatter.Legend(3, pscatter->GetLegend(id) + String("-") +
738 (show && tabFitRight.opCuadratic ? cuadratic.GetEquation(tabFitRight.numDecimals) : String(t_("Cuadratic"))));
739 tabFitLeft.scatter.Legend(4, pscatter->GetLegend(id) + String("-") +
740 (show && tabFitRight.opCubic ? cubic.GetEquation(tabFitRight.numDecimals) : String(t_("Cubic"))));
741 tabFitLeft.scatter.Legend(5, pscatter->GetLegend(id) + String("-") +
742 (show && tabFitRight.opSinus ? sinus.GetEquation(tabFitRight.numDecimals) : String(t_("Sinusoidal"))));
743 tabFitLeft.scatter.Legend(6, pscatter->GetLegend(id) + String("-") +
744 (show && tabFitRight.opSinusTend ? sinusTend.GetEquation(tabFitRight.numDecimals) : String(t_("Sinusoidal tend"))));
745 tabFitLeft.scatter.Legend(7, pscatter->GetLegend(id) + String("-") + String(t_("Spline")));
746 tabFitLeft.scatter.Legend(8, pscatter->GetLegend(id) + String("-") + String(Format(t_("Der_%d"), ~tabFitRight.derOrder)));
747 tabFitLeft.scatter.Legend(9, pscatter->GetLegend(id) + String("-") + String(Format(t_("S_G_%d"), ~tabFitRight.sgOrder)));
748 tabFitLeft.scatter.Refresh();
749 }
750
OnFFT()751 void ProcessingTab::OnFFT()
752 {
753 String errText;
754 tabFreqLeft.scatter.RemoveAllSeries();
755 double samplingTime = tabFreqRight.samplingTime;
756 if (samplingTime < 0) {
757 Exclamation(t_("Incorrect sampling time"));
758 return;
759 }
760 int64 idMaxFFT;
761 {
762 WaitCursor waitcursor;
763
764 if (tabFitLeft.scatter.IsDeletedDataSource(0))
765 return;
766 DataSource &data = tabFitLeft.scatter.GetDataSource(0);
767
768 Vector<Pointf> orderedSeries;
769 for (int64 i = 0; i < data.GetCount(); ++i) { // Clean Nulls
770 if (!IsNull(data.x(i)) && !IsNull(data.y(i)))
771 orderedSeries << Pointf(data.x(i), data.y(i));
772 }
773 //if (orderedSeries.GetCount() != data.GetCount())
774 // errText << Format(t_("Removed %d Null points."), data.GetCount() - orderedSeries.GetCount());
775
776 PointfLess less;
777 Sort(orderedSeries, less);
778
779 Vector<double> resampledSeries;
780 resampledSeries << orderedSeries[0].y;
781 double nextSample = orderedSeries[0].x + samplingTime;
782 for (int i = 0; i < orderedSeries.GetCount() - 1;) {
783 if (orderedSeries[i].x == nextSample) {
784 resampledSeries << orderedSeries[i].y;
785 nextSample += samplingTime;
786 } else if (orderedSeries[i].x < nextSample && orderedSeries[i + 1].x > nextSample) { // Linear interpolation
787 resampledSeries << (orderedSeries[i].y + (orderedSeries[i + 1].y - orderedSeries[i].y)*
788 (nextSample - orderedSeries[i].x)/(orderedSeries[i + 1].x - orderedSeries[i].x));
789 nextSample += samplingTime;
790 } else
791 ++i;
792 }
793 if (orderedSeries[orderedSeries.GetCount() - 1].x == nextSample)
794 resampledSeries << orderedSeries[orderedSeries.GetCount() - 1].y;
795
796 VectorY<double> series(resampledSeries, 0, samplingTime);
797 fft = series.FFTY(samplingTime, tabFreqRight.opXAxis == 1, tabFreqRight.type,
798 tabFreqRight.window.GetIndex(), tabFreqRight.num, tabFreqRight.overlapping);
799 VectorPointf fftData(fft);
800 double mxy = fftData.MaxY(idMaxFFT);
801 if (!IsNull(mxy))
802 tabFreqRight.eMax <<= Format("(%f,%f)", fftData.x(idMaxFFT), mxy);
803
804 if (tabFreqRight.type == DataSource::T_PSD) {
805 double m_1, m0, m1, m2;
806 fftData.GetSpectralMomentsY(tabFreqRight.opXAxis == 1, m_1, m0, m1, m2);
807 tabFreqRight.m_1 <<= FormatDouble(m_1);
808 tabFreqRight.m0 <<= FormatDouble(m0);
809 tabFreqRight.m1 <<= FormatDouble(m1);
810 tabFreqRight.m2 <<= FormatDouble(m2);
811 } else {
812 tabFreqRight.m_1 <<= "";
813 tabFreqRight.m0 <<= "";
814 tabFreqRight.m1 <<= "";
815 tabFreqRight.m2 <<= "";
816 }
817 tabFreqRight.m_1.Enable(tabFreqRight.type == DataSource::T_PSD);
818 tabFreqRight.m0.Enable(tabFreqRight.type == DataSource::T_PSD);
819 tabFreqRight.m1.Enable(tabFreqRight.type == DataSource::T_PSD);
820 tabFreqRight.m2.Enable(tabFreqRight.type == DataSource::T_PSD);
821 tabFreqRight.labSpectral.Enable(tabFreqRight.type == DataSource::T_PSD);
822 }
823 if (fft.IsEmpty()) {
824 tabFreqLeft.comments.SetText(errText);
825 Exclamation(t_("Error obtaining FFT"));
826 return;
827 }
828
829 String strtype;
830 switch(tabFreqRight.type) {
831 case DataSource::T_FFT: strtype = t_("FFT"); break;
832 case DataSource::T_PHASE: strtype = t_("FFT-phase [rad]"); break;
833 case DataSource::T_PSD: strtype = t_("Power Spectral Density"); break;
834 }
835 String legend = tabFitLeft.scatter.GetLegend(0) + String("-") + strtype;
836 tabFreqLeft.scatter.AddSeries(fft).Legend(legend);
837 tabFreqLeft.scatter.ShowInfo().ShowContextMenu().ShowProcessDlg().ShowPropertiesDlg().SetMouseHandlingLinked(true, true);
838 tabFreqLeft.scatter.SetLabelX(tabFreqRight.opXAxis == 1 ? t_("Frequency [Hz]") : t_("Period [sec]"));
839 tabFreqLeft.scatter.SetLabelY(legend);
840 tabFreqLeft.scatter.ZoomToFit(true, true);
841 if (idMaxFFT > 0 && fft[int(idMaxFFT)].x < (fft[fft.GetCount() - 1].x)/2)
842 tabFreqLeft.scatter.SetRange(fft[int(idMaxFFT)].x*2, Null);
843
844 tabFreqLeft.comments.SetText(errText);
845 }
846
OnHist()847 void ProcessingTab::OnHist() {
848 tabHistLeft.scatter.RemoveAllSeries();
849
850 if (tabFitLeft.scatter.IsDeletedDataSource(0))
851 return;
852 DataSource &data = tabFitLeft.scatter.GetDataSource(0);
853
854 double minVal = ~tabHistRight.minVal;
855 double maxVal = ~tabHistRight.maxVal;
856 if (minVal >= maxVal) {
857 Exclamation(Format(t_("Min val %d has to be lower than Max val %f"), minVal, maxVal));
858 return;
859 }
860 int numVals = ~tabHistRight.numVals;
861 bool normalize = ~tabHistRight.opNormalize;
862 double valNormalize = ~tabHistRight.valNormalize;
863 bool isY = ~tabHistRight.axis == t_("Y");
864
865 histogram.Create(data, minVal, maxVal, numVals, isY).Accumulative(~tabHistRight.opAccumulative);
866
867 if (normalize)
868 histogram.Normalize(valNormalize);
869
870 String legend = tabFitLeft.scatter.GetLegend(0) + String("-") + t_("Histogram");
871 tabHistLeft.scatter.AddSeries(histogram).Legend(legend).NoMark().
872 Units(normalize ? "x" + FormatDouble(valNormalize) : "#", isY ?
873 tabFitLeft.scatter.GetUnitsY(0) :
874 tabFitLeft.scatter.GetUnitsX(0));
875 if (~tabHistRight.opStaggered)
876 tabHistLeft.scatter.PlotStyle<StaggeredSeriesPlot>().Dash("").NoMark().Fill(Blue()).Opacity(0.3).Stroke(2, LtBlue());
877 tabHistLeft.scatter.ShowInfo().ShowContextMenu().ShowProcessDlg().ShowPropertiesDlg().SetMouseHandlingLinked(true, true);
878 tabHistLeft.scatter.SetLabelX(isY ? tabFitLeft.scatter.GetLegend(0) : tabFitLeft.scatter.GetLabelX());
879 tabHistLeft.scatter.SetLabelY(t_("Number"));
880 tabHistLeft.scatter.ZoomToFit(true, true);
881 double ymax = tabHistLeft.scatter.GetYMax();
882 tabHistLeft.scatter.SetXYMin(Null, 0);
883 tabHistLeft.scatter.SetRange(Null, ymax);
884 }
885
886 }