REMOVED: the DrawModel function from classifierInterface (they all do exactly the...
[mldemos:baraks-mldemos.git] / MLDemos / visualization.cpp
1 #include "visualization.h"
2 #include "ui_visualization.h"
3
4 #include <algorithm>
5 #include <canvas.h>
6 #include <QPainter>
7 #include <QBitmap>
8 #include <QDebug>
9 using namespace std;
10
11 Visualization::Visualization(Canvas *canvas, QWidget *parent) :
12     QWidget(parent),
13     ui(new Ui::Visualization), spacer(0),
14     canvas(canvas), data(canvas->data)
15 {
16     ui->setupUi(this);
17     connect(ui->typeCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(OptionsChanged()));
18     connect(ui->grayscaleCheck, SIGNAL(clicked()), this, SLOT(OptionsChanged()));
19     connect(ui->updateButton, SIGNAL(clicked()), this, SLOT(Update()));
20     connect(ui->flavorCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(Update()));
21     connect(ui->inputCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(Update()));
22     connect(ui->x1Combo, SIGNAL(currentIndexChanged(int)), this, SLOT(Update()));
23     connect(ui->x2Combo, SIGNAL(currentIndexChanged(int)), this, SLOT(Update()));
24     connect(ui->x3Combo, SIGNAL(currentIndexChanged(int)), this, SLOT(Update()));
25     connect(ui->grayscaleCheck, SIGNAL(clicked()), this, SLOT(Update()));
26     ui->display->installEventFilter(this);
27     spacer = new QWidget();
28     spacer->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
29     OptionsChanged();
30 }
31
32 Visualization::~Visualization()
33 {
34     delete ui;
35 }
36
37 bool Visualization::eventFilter(QObject *obj, QEvent *event)
38 {
39     if(obj == ui->display && event->type() == QEvent::MouseButtonDblClick)
40     {
41         if(ui->optionsWidget->isVisible()) ui->optionsWidget->hide();
42         else ui->optionsWidget->show();
43         adjustSize();
44         Update();
45         return true;
46     }
47     if(obj == ui->display && event->type() == QEvent::Paint)
48     {
49         QPainter painter;
50         painter.begin(ui->display);
51         if(displayPixmap.isNull())
52         {
53             painter.fillRect(0,0,ui->display->width(), ui->display->height(), Qt::white);
54         }
55         else
56         {
57             painter.drawPixmap(0,0, ui->display->width(), ui->display->height(), displayPixmap, 0,0,displayPixmap.width(), displayPixmap.height());
58         }
59         return true;
60     }
61     return false;
62 }
63
64 void Visualization::resizeEvent(QResizeEvent *event)
65 {
66     Update();
67 }
68
69 void Visualization::OptionsChanged()
70 {
71     int index = ui->typeCombo->currentIndex();
72     ui->flavorCombo->hide();
73     ui->zoomSlider->hide();
74     ui->axesWidget->hide();
75     ui->inputCombo->hide();
76     switch(index)
77     {
78     case 0: // samples: scatterplots
79         ui->zoomSlider->show();
80         break;
81     case 1: // samples: parallel coordinates
82         break;
83     case 2: // samples: radial graphs
84         break;
85     case 3: // samples: andrews plots
86         break;
87     case 4: // samples: bubble plots
88         ui->axesWidget->show();
89         break;
90     case 5: // distribution: Individual plots
91     {
92         ui->inputCombo->show();
93         ui->flavorCombo->blockSignals(true);
94         ui->flavorCombo->clear();
95         ui->flavorCombo->addItem("Histograms");
96         ui->flavorCombo->addItem("Box Plots");
97         ui->flavorCombo->addItem("HDR Box Plots");
98         ui->flavorCombo->addItem("Box Percentile Plots");
99         ui->flavorCombo->addItem("Violin Plots");
100         ui->flavorCombo->addItem("Raws");
101         ui->flavorCombo->addItem("Star Plots");
102         ui->flavorCombo->setCurrentIndex(1);
103         ui->flavorCombo->show();
104         ui->flavorCombo->blockSignals(false);
105     }
106         break;
107     case 6: // distribution: splatter plots
108         ui->axesWidget->show();
109         ui->flavorCombo->blockSignals(true);
110         ui->flavorCombo->clear();
111         ui->flavorCombo->addItem("Jet Colormap");
112         ui->flavorCombo->addItem("Autumn");
113         ui->flavorCombo->addItem("Heat");
114         ui->flavorCombo->setCurrentIndex(0);
115         ui->flavorCombo->show();
116         ui->flavorCombo->blockSignals(false);
117         break;
118     case 7: // distribution: Correlation
119     {
120         ui->axesWidget->hide();
121         ui->flavorCombo->blockSignals(true);
122         ui->flavorCombo->clear();
123         ui->flavorCombo->addItem("Scatterplot Circles");
124         ui->flavorCombo->addItem("Scatterplot Ellipses");
125         ui->flavorCombo->addItem("Scatterplot Graphs");
126         ui->flavorCombo->setCurrentIndex(0);
127         ui->flavorCombo->show();
128         ui->flavorCombo->blockSignals(false);
129     }
130         break;
131     }
132     Update();
133 }
134
135 void Visualization::Update()
136 {
137     int index = ui->typeCombo->currentIndex();
138     displayPixmap = QPixmap();
139     ui->display->setMinimumSize(0,0);
140     ui->display->setMaximumSize(65536,65536);
141     ui->scrollAreaWidgetContents->layout()->removeWidget(spacer);
142     ui->scrollAreaWidgetContents->adjustSize();
143
144     switch(index)
145     {
146     case 0: // samples: scatterplots
147         GenerateScatterPlot();
148         break;
149     case 1: // samples: parallel coordinates
150         GenerateParallelCoords();
151         break;
152     case 2: // samples: radial graphs
153         GenerateRadialGraph();
154         break;
155     case 3: // samples: andrews plots
156         GenerateAndrewsPlot();
157         break;
158     case 4: // samples: bubble plots
159         GenerateBubblePlot();
160         break;
161     case 5: // distribution: Individual plots
162         ui->scrollAreaWidgetContents->layout()->addWidget(spacer);
163         //ui->scrollAreaWidgetContents->layout()->;
164         ui->scrollAreaWidgetContents->adjustSize();
165         GenerateIndividualPlot();
166         break;
167     case 6: // distribution: splatter plots
168         GenerateSplatterPlot();
169         break;
170     case 7: // distribution: Correlation
171         GenerateCorrelationPlot();
172         break;
173     }
174     ui->display->repaint();
175 }
176
177 void Visualization::UpdateDims()
178 {
179     int dims = data->GetDimCount();
180     int x1 = ui->x1Combo->currentIndex();
181     int x2 = ui->x2Combo->currentIndex();
182     int x3 = ui->x3Combo->currentIndex();
183     ui->x1Combo->blockSignals(true);
184     ui->x2Combo->blockSignals(true);
185     ui->x3Combo->blockSignals(true);
186     ui->x1Combo->clear();
187     ui->x2Combo->clear();
188     ui->x3Combo->clear();
189     QStringList dimNames = canvas->dimNames;
190     if(dimNames.size() == 0)
191     {
192         FOR(i, dims)
193         {
194             dimNames << QString("Dimension %1").arg(i+1);
195         }
196     }
197     dimNames << QString("none");
198     FOR(i, dimNames.size())
199     {
200         ui->x1Combo->addItem(dimNames[i]);
201         ui->x2Combo->addItem(dimNames[i]);
202         ui->x3Combo->addItem(dimNames[i]);
203     }
204     if(x1 >= 0 && x1 < dims) ui->x1Combo->setCurrentIndex(x1);
205     else ui->x1Combo->setCurrentIndex(min(dims+1,0));
206     if(x1 >= 0 && x2 < dims) ui->x2Combo->setCurrentIndex(x2);
207     else ui->x2Combo->setCurrentIndex(min(dims+1,1));
208     if(x1 >= 0 && x3 < dims) ui->x3Combo->setCurrentIndex(x3);
209     else ui->x3Combo->setCurrentIndex(min(dims+1,2));
210     ui->x1Combo->blockSignals(false);
211     ui->x2Combo->blockSignals(false);
212     ui->x3Combo->blockSignals(false);
213 }
214
215 void Visualization::GenerateAndrewsPlot()
216 {
217     std::vector<fvec> samples = data->GetSamples();
218     ivec labels = data->GetLabels();
219     if(!samples.size()) return;
220     int dim = samples[0].size();
221
222     fvec mins(dim, FLT_MAX), maxes(dim, -FLT_MIN);
223     FOR(d, dim)
224     {
225         FOR(i, samples.size())
226         {
227             mins[d] = min(mins[d], samples[i][d]);
228             maxes[d] = max(maxes[d], samples[i][d]);
229         }
230     }
231
232     int pad = 20;
233     int mapW = (ui->scrollArea->width()-12) - pad*2, mapH = (ui->scrollArea->height()-12) - pad*2;
234
235     ui->scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
236     ui->scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
237     QPixmap pixmap = QPixmap(ui->scrollArea->width()-2, ui->scrollArea->height()-2);
238     pixmap.fill(Qt::white);
239     QPainter painter(&pixmap);
240     painter.setRenderHint(QPainter::Antialiasing);
241     float radius = min(mapW, mapH)/3.f;
242     QPointF center(mapW*0.5f, mapH*0.5f);
243     QPointF old;
244     painter.setPen(Qt::black);
245
246     // f(t) = x0/sqrt(2) + x1*sin(t) + x2*cos(t) + x3*sin(2t) + x4*cos(2t) + x5*sin(3t) + x6*cos(3t) + x7*sin(4t)
247     vector<fvec> values(samples.size());
248     const int steps = 200;
249     float minv=FLT_MAX, maxv=-FLT_MAX;
250     FOR(i, samples.size())
251     {
252         values[i].resize(steps);
253         FOR(j, steps)
254         {
255             float t = j/(float)steps*(M_PI*2) - M_PI;
256             float value = 0;
257             FOR(d, dim)
258             {
259                 float v = (samples[i][d]-mins[d])/(maxes[d]-mins[d]);
260                 if(!d) value += v*sqrtf(2.f);
261                 else
262                 {
263
264                     value += v * (d%2 ? sin(t*((d+1)/2)) : cos(t*((d+1)/2)));
265                 }
266             }
267             values[i][j] = value;
268             minv = min(minv, value);
269             maxv = max(maxv, value);
270         }
271     }
272
273     FOR(i, values.size())
274     {
275         FOR(j, values[i].size())
276         {
277             float value = (values[i][j]-minv)/(maxv-minv);
278             QPointF point = QPointF(j*pixmap.width()/steps, value*mapH + pad);
279             QColor color;
280             if(ui->grayscaleCheck->isChecked())
281             {
282                 int gray = (labels[i]%SampleColorCnt)*255/SampleColorCnt;
283                 color = QColor(gray,gray,gray);
284             }
285             else color = SampleColor[labels[i]%SampleColorCnt];
286             if(labels[i]==0) color = Qt::black;
287             painter.setPen(QPen(color,0.5));
288             if(j) painter.drawLine(point, old);
289             old = point;
290         }
291     }
292     displayPixmap = pixmap;
293 }
294
295
296 void Visualization::GenerateRadialGraph()
297 {
298     std::vector<fvec> samples = data->GetSamples();
299     ivec labels = data->GetLabels();
300     if(!samples.size()) return;
301     int dim = samples[0].size();
302
303     fvec mins(dim, FLT_MAX), maxes(dim, -FLT_MIN);
304     FOR(d, dim)
305     {
306         FOR(i, samples.size())
307         {
308             mins[d] = min(mins[d], samples[i][d]);
309             maxes[d] = max(maxes[d], samples[i][d]);
310         }
311     }
312
313     int pad = 20;
314     int mapW = (ui->scrollArea->width()-12) - pad*2, mapH = (ui->scrollArea->height()-12) - pad*2;
315
316     ui->scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
317     ui->scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
318     QPixmap pixmap = QPixmap(ui->scrollArea->width()-2, ui->scrollArea->height()-2);
319     pixmap.fill(Qt::white);
320     QPainter painter(&pixmap);
321     painter.setRenderHint(QPainter::Antialiasing);
322     float radius = min(mapW, mapH)/3.f;
323     QPointF center(mapW*0.5f, mapH*0.5f);
324     QPointF old;
325     painter.setPen(Qt::black);
326     FOR(d, dim)
327     {
328         float theta = d/(float)(dim)*2*M_PI;
329         QPointF point = QPointF(cos(theta), sin(theta))*radius;
330         if(d) painter.drawLine(center + point, center + old);
331         painter.drawText(center + point*1.1, QString("e%1").arg(d+1));
332         old = point;
333     }
334     painter.drawLine(center + QPointF(1.f, 0.f)*radius, center + old);
335
336     painter.setRenderHint(QPainter::Antialiasing);
337     FOR(i, samples.size())
338     {
339         QPointF samplePoint;
340         float dimSum = 0;
341         FOR(d, dim)
342         {
343             float theta = d/(float)(dim)*2*M_PI;
344             QPointF point = QPointF(cos(theta), sin(theta))*radius;
345             float value = (samples[i][d]-mins[d])/(maxes[d]-mins[d]);
346             samplePoint += point*value;
347             dimSum += value;
348         }
349         samplePoint /= dimSum;
350         float drawRadius = 7;
351         Canvas::drawSample(painter, center + samplePoint, drawRadius, labels[i]);
352         QColor color;
353         if(ui->grayscaleCheck->isChecked())
354         {
355             int gray = (labels[i]%SampleColorCnt)*255/SampleColorCnt;
356             color = QColor(gray,gray,gray);
357         }
358         else color = SampleColor[labels[i]%SampleColorCnt];
359         painter.setPen(color);
360     }
361     displayPixmap = pixmap;
362 }
363
364 void Visualization::GenerateParallelCoords()
365 {
366     std::vector<fvec> samples = data->GetSamples();
367     ivec labels = data->GetLabels();
368     if(!samples.size()) return;
369     int dim = samples[0].size();
370
371     fvec mins(dim, FLT_MAX), maxes(dim, -FLT_MIN);
372     FOR(d, dim)
373     {
374         FOR(i, samples.size())
375         {
376             mins[d] = min(mins[d], samples[i][d]);
377             maxes[d] = max(maxes[d], samples[i][d]);
378         }
379     }
380
381     int pad = 20;
382     int mapW = (ui->scrollArea->width()-12) - pad*2, mapH = (ui->scrollArea->height()-12) - pad*2;
383
384     ui->scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
385     ui->scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
386     QPixmap pixmap = QPixmap(ui->scrollArea->width(), ui->scrollArea->height());
387     pixmap.fill(Qt::white);
388     QPainter painter(&pixmap);
389     FOR(d, dim)
390     {
391         float x = d*mapW/(float)(dim-1) + pad;
392         painter.setPen(Qt::black);
393         painter.drawLine(x, pad, x, mapH+pad);
394         painter.drawText(x-10, mapH+2*pad-4, QString("e%1").arg(d+1));
395     }
396
397     painter.setRenderHint(QPainter::Antialiasing);
398     FOR(i, samples.size())
399     {
400         QPointF old;
401         FOR(d, dim)
402         {
403             float x = d*mapW/(float)(dim-1) + pad;
404             float y = samples[i][d];
405             y = (y-mins[d])/(maxes[d] - mins[d]);
406             QPointF point(x,pad + y*mapH);
407             float radius = 7;
408             Canvas::drawSample(painter, point, radius, labels[i]);
409             QColor color;
410             if(ui->grayscaleCheck->isChecked())
411             {
412                 int gray = (labels[i]%SampleColorCnt)*255/SampleColorCnt;
413                 color = QColor(gray,gray,gray);
414             }
415             else color = SampleColor[labels[i]%SampleColorCnt];
416             if(labels[i]==0) color = Qt::black;
417             painter.setPen(color);
418             if(d) painter.drawLine(point, old);
419             old = point;
420         }
421     }
422     displayPixmap = pixmap;
423 }
424
425 void Visualization::GenerateScatterPlot(bool bCheckOnly)
426 {
427     std::vector<fvec> samples = data->GetSamples();
428     ivec labels = data->GetLabels();
429     if(!samples.size()) return;
430     int dim = samples[0].size();
431     bool bEvenCount = dim%2 == 1;
432     int gridX = dim;
433     int gridY = dim;
434
435     fvec mins(dim, FLT_MAX), maxes(dim, -FLT_MIN);
436     FOR(d, dim)
437     {
438         FOR(i, samples.size())
439         {
440             mins[d] = min(mins[d], samples[i][d]);
441             maxes[d] = max(maxes[d], samples[i][d]);
442         }
443     }
444
445     int pad = 20;
446     int mapW = (ui->scrollArea->width()-12)/gridX - pad*2;
447     int mapH = (ui->scrollArea->height()-12)/gridX - pad*2;
448
449     bool bScroll = false;
450     if(mapW < 100 || mapH < 100)
451     {
452         bScroll = true;
453         mapW = max(mapW, 100);
454         mapH = max(mapH, 100);
455     }
456     if(bScroll && bCheckOnly) return;
457
458     QList<QPixmap> maps;
459     FOR(index0, dim)
460     {
461         FOR(index1, dim)
462         {
463             QPixmap map(mapW + 2*pad,mapH + 2*pad);
464             int w = map.width() - 2*pad, h = map.height() - 2*pad;
465             map.fill(Qt::white);
466             QPainter painter(&map);
467             painter.setRenderHint(QPainter::Antialiasing);
468
469             FOR(i, samples.size())
470             {
471                 float x = samples[i][index0];
472                 float y = samples[i][index1];
473                 x = (x-mins[index0])/(maxes[index0] - mins[index0]);
474                 y = (y-mins[index1])/(maxes[index1] - mins[index1]);
475                 QPointF point(y*w + pad, x*h + pad);
476                 float radius = 5;
477                 if(ui->grayscaleCheck->isChecked())
478                 {
479                     int gray = (labels[i]%SampleColorCnt)*255/SampleColorCnt;
480                     QColor color = QColor(gray,gray,gray);
481                     painter.setBrush(color);
482                     painter.setPen(Qt::black);
483                     painter.drawEllipse(point, radius, radius);
484                 }
485                 else Canvas::drawSample(painter, point, radius, labels[i]);
486             }
487             painter.setBrush(Qt::NoBrush);
488             painter.setPen(Qt::black);
489             painter.setRenderHint(QPainter::Antialiasing, false);
490             painter.drawRect(pad/2,pad/2,w+pad, h+pad);
491             painter.drawText(pad/2+1, map.height()-pad/2-1, QString("e%1 x e%2").arg(index1+1).arg(index0+1));
492             maps.push_back(map);
493         }
494     }
495     QPixmap pixmap;
496
497     if(bScroll)
498     {
499         pixmap = QPixmap((mapW+2*pad)*gridX, (mapH+2*pad)*gridY);
500         ui->scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
501         ui->scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
502     }
503     else
504     {
505         pixmap = QPixmap(ui->scrollArea->width(), ui->scrollArea->height());
506         ui->scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
507         ui->scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
508     }
509     pixmap.fill(Qt::white);
510     QPainter painter(&pixmap);
511     FOR(i, maps.size())
512     {
513         int xIndex = i%gridX;
514         int yIndex = i/gridX;
515         painter.drawPixmap(QPoint(xIndex*pixmap.width()/gridX, yIndex*pixmap.height()/gridY), maps[i]);
516     }
517     displayPixmap = pixmap;
518     ui->display->setMinimumSize(displayPixmap.size());
519     ui->display->setMaximumSize(displayPixmap.size());
520 }
521
522 void Visualization::GenerateBubblePlot()
523 {
524     std::vector<fvec> samples = data->GetSamples();
525     ivec labels = data->GetLabels();
526     if(!samples.size()) return;
527     int dim = samples[0].size();
528     if(!dim) return;
529     int xIndex = ui->x1Combo->currentIndex();
530     int yIndex = ui->x2Combo->currentIndex();
531     int zIndex = ui->x3Combo->currentIndex();
532     if(xIndex >= dim) xIndex = 0;
533     if(yIndex >= dim) yIndex = min(dim-1,0);
534     if(zIndex >= dim) zIndex = -1;
535
536     fvec mins(dim, FLT_MAX), maxes(dim, -FLT_MIN);
537     FOR(d, dim)
538     {
539         FOR(i, samples.size())
540         {
541             mins[d] = min(mins[d], samples[i][d]);
542             maxes[d] = max(maxes[d], samples[i][d]);
543         }
544     }
545     fvec diffs = maxes-mins;
546
547     int pad = 20;
548     int mapW = (ui->scrollArea->width()-12) - pad*2, mapH = (ui->scrollArea->height()-12) - pad*2;
549
550     ui->scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
551     ui->scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
552     QPixmap pixmap = QPixmap(ui->scrollArea->width()-2, ui->scrollArea->height()-2);
553     pixmap.fill(Qt::white);
554
555     QPainter painter(&pixmap);
556     painter.drawLine(pad, mapH+pad, mapW+pad, mapH+pad);
557     painter.drawLine(pad, pad, pad, mapH+pad);
558
559     if(zIndex == -1)
560     {
561         srand48(0);
562         srand(0);
563     }
564     painter.setRenderHint(QPainter::Antialiasing);
565     FOR(i, samples.size())
566     {
567         float x = (samples[i][xIndex]-mins[xIndex])/(diffs[xIndex]);
568         float y = (samples[i][yIndex]-mins[yIndex])/(diffs[yIndex]);
569         QPointF point(x*mapW + pad,y*mapH + pad);
570
571         float radius = 10;
572         if(zIndex != -1)
573         {
574             radius = (samples[i][zIndex]-mins[zIndex])/(diffs[zIndex]);
575             radius = radius*60 + 3;
576         }
577         else radius = drand48()*40 + 3;
578
579         QColor color;
580         if(ui->grayscaleCheck->isChecked())
581         {
582             int gray = (labels[i]%SampleColorCnt)*255/SampleColorCnt;
583             color = QColor(gray,gray,gray);
584         }
585         else color = SampleColor[labels[i]%SampleColorCnt];
586         if(labels[i] == 0) color = Qt::lightGray;
587         painter.setBrush(color);
588         painter.setPen(Qt::black);
589         painter.setOpacity(0.5f);
590         painter.drawEllipse(QRectF(point.x()-radius/2.,point.y()-radius/2.,radius,radius));
591     }
592
593     displayPixmap = pixmap;
594 }
595
596 void Visualization::GenerateIndividualPlot()
597 {
598     std::vector<fvec> samples = data->GetSamples();
599     ivec labels = data->GetLabels();
600     if(!samples.size()) return;
601     int classCount = data->GetClassCount(labels);
602     int dim = samples[0].size();
603
604     int inputType = ui->inputCombo->currentIndex();
605     int flavorType = ui->flavorCombo->currentIndex();
606
607     fvec mins(dim,FLT_MAX), maxes(dim,-FLT_MAX);
608
609     // we parse the data to gather density information on each class and dimension
610     map<int,vector< pair<fvec,fvec> > > classGraphData;
611     vector< map<int,fvec> > dimClassData;
612     FOR(d, dim)
613     {
614         map<int,fvec> classData;
615         FOR(i, samples.size())
616         {
617             float v = samples[i][d];
618             classData[labels[i]].push_back(v);
619             if(mins[d]>v) mins[d] = v;
620             if(maxes[d]<v) maxes[d] = v;
621         }
622         if(mins[d] == maxes[d])
623         {
624             mins[d] = mins[d]/2;
625             maxes[d] = mins[d]*3/2;
626         }
627         FORIT(classData, int, fvec)
628         {
629             if(!classGraphData.count(it->first))
630             {
631                 classGraphData[it->first].resize(dim);
632             }
633             fvec box = BoxPlot(it->second);
634             fvec density = Density(it->second, box[4], box[0], (flavorType==3?15:31)); // 3: violin plot
635             classGraphData[it->first][d] = make_pair(box, density);
636         }
637         dimClassData.push_back(classData);
638     }
639
640     // we go through all the data to find the minima and maxima for each class
641     //fvec mins(dim,FLT_MAX), maxes(dim,-FLT_MAX);
642     map<int,fvec> dimMins, dimMaxes;
643     FOR(i, samples.size())
644     {
645         int c = labels[i];
646         if(!dimMins.count(c)) dimMins[c] = fvec(dim,FLT_MAX);
647         if(!dimMaxes.count(c)) dimMaxes[c] = fvec(dim,-FLT_MAX);
648         FOR(d, dim)
649         {
650             float v = samples[i][d];
651             //if(mins[d] > v) mins[d] = v;
652             //if(maxes[d] < v) maxes[d] = v;
653             if(dimMins[c][d] > v) dimMins[c][d] = v;
654             if(dimMaxes[c][d] < v) dimMaxes[c][d] = v;
655         }
656     }
657
658     // we choose the proper size for each individual graph
659     int pad = 20;
660     int w,h;
661     if(flavorType < 6)
662     {
663         w = 80;
664         h = 220;
665     }
666     else // star plots
667     {
668         w = 100;
669         h = 120;
670     }
671
672     int mapW = ui->scrollArea->width()-12;
673     int mapH = ui->scrollArea->height()-12;
674
675     // depending on how many classes/dimensions we have, we adjust the grid count
676     int gridX, gridY;
677     int count, perItemCount;
678     int totalCount = dim*classCount;
679     switch(inputType)
680     {
681     case 0: // dimension
682         count = dim;
683         perItemCount = classCount;
684         break;
685     case 1: // class
686         count = classCount;
687         perItemCount = dim;
688         break;
689     }
690     if(flavorType == 6)
691     {
692         totalCount = count;
693         perItemCount = 1;
694     }
695     gridX = max(1,min(count,(mapW/(w*perItemCount+pad*2))));
696     gridY = max(1,count/gridX + (count%gridX ? 1 : 0) );
697     if(mapW<(w*perItemCount+pad)*gridX) mapW = (w*perItemCount+pad)*gridX;
698     mapH = gridY*h;
699
700     // and we set the size of the pixmap
701     QPixmap pixmap(mapW, mapH);
702     QBitmap bitmap(mapW, mapH);
703     pixmap.setMask(bitmap);
704     pixmap.fill(Qt::transparent);
705     QPainter painter(&pixmap);
706     painter.setRenderHint(QPainter::Antialiasing);
707     painter.setPen(QPen(Qt::black, 2));
708
709     painter.setPen(Qt::black);
710     FOR(i, count)
711     {
712         QPixmap smallPix = GetGraphPixmap(flavorType, inputType, dim, classCount,
713                                           i, w, h,
714                                           classGraphData, dimClassData,
715                                           mins, dimMins,
716                                           maxes, dimMaxes);
717         painter.setBrush(SampleColor[i%SampleColorCnt]);
718         QPoint tl((i%gridX)*(w*perItemCount+pad), (i/gridX)*h);
719         painter.drawRect(tl.x(), tl.y(), w*perItemCount+pad, h);
720         painter.drawPixmap(tl, smallPix);
721     }
722
723     displayPixmap = pixmap;
724     ui->display->setMinimumSize(displayPixmap.size());
725     ui->display->setMaximumSize(displayPixmap.size());
726     ui->display->adjustSize();
727     ui->scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
728     ui->scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
729 }
730
731 void Visualization::GenerateSplatterPlot()
732 {
733     std::vector<fvec> samples = data->GetSamples();
734     ivec labels = data->GetLabels();
735     if(!samples.size()) return;
736     int dim = samples[0].size();
737     if(!dim) return;
738     int xIndex = ui->x1Combo->currentIndex();
739     int yIndex = ui->x2Combo->currentIndex();
740     int zIndex = ui->x3Combo->currentIndex();
741     if(xIndex >= dim) xIndex = 0;
742     if(yIndex >= dim) yIndex = min(dim-1,0);
743     if(zIndex >= dim) zIndex = -1;
744
745     fvec mins(dim, FLT_MAX), maxes(dim, -FLT_MIN);
746     FOR(d, dim)
747     {
748         FOR(i, samples.size())
749         {
750             mins[d] = min(mins[d], samples[i][d]);
751             maxes[d] = max(maxes[d], samples[i][d]);
752         }
753     }
754     fvec diffs = maxes-mins;
755
756     int pad = 20;
757     int mapW = (ui->scrollArea->width()-12) - pad*2, mapH = (ui->scrollArea->height()-12) - pad*2;
758
759     ui->scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
760     ui->scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
761     QPixmap pixmap = QPixmap(ui->scrollArea->width(), ui->scrollArea->height());
762     pixmap.fill(Qt::white);
763
764     QPainter painter(&pixmap);
765     painter.drawLine(pad, mapH+pad, mapW+pad, mapH+pad);
766     painter.drawLine(pad, pad, pad, mapH+pad);
767
768     painter.setRenderHint(QPainter::Antialiasing);
769     FOR(i, samples.size())
770     {
771         float x = (samples[i][xIndex]-mins[xIndex])/(diffs[xIndex]);
772         float y = (samples[i][yIndex]-mins[yIndex])/(diffs[yIndex]);
773         QPointF point(x*mapW + pad,y*mapH + pad);
774
775         float radius = 10;
776         if(zIndex != -1)
777         {
778             radius = (samples[i][zIndex]-mins[zIndex])/(diffs[zIndex]);
779             radius = radius*60 + 3;
780         }
781         else
782         {
783             radius = drand48()*40 + 3;
784         }
785
786         QColor color;
787         if(ui->grayscaleCheck->isChecked())
788         {
789             int gray = (labels[i]%SampleColorCnt)*255/SampleColorCnt;
790             color = QColor(gray,gray,gray);
791         }
792         else color = SampleColor[labels[i]%SampleColorCnt];
793         if(labels[i] == 0) color = Qt::lightGray;
794         painter.setBrush(color);
795         painter.setPen(Qt::black);
796         painter.setOpacity(0.5f);
797         painter.drawEllipse(QRectF(point.x()-radius/2.,point.y()-radius/2.,radius,radius));
798     }
799
800     displayPixmap = pixmap;
801 }
802
803 void Visualization::GenerateCorrelationPlot()
804 {
805
806 }
807
808 QPixmap Visualization::GetGraphPixmap(int type, int inputType, int dim, int classCount,
809                                       int index, int w, int h,
810                                       std::map<int,std::vector< std::pair<fvec,fvec> > > classGraphData,
811                                       std::vector< std::map<int,fvec> > dimClassData,
812                                       fvec mins, std::map<int,fvec> dimMins,
813                                       fvec maxes, std::map<int,fvec> dimMaxes)
814 {
815     if(type == 6) return GetStarPixmap(inputType, dim, classCount, index, w, h, mins, dimMins, maxes, dimMaxes);
816
817     int perItemCount;
818     int d;
819     int cCnt;
820     switch(inputType)
821     {
822     case 0:
823         perItemCount = classCount;
824         d = index;
825         cCnt = 0;
826         break;
827     case 1:
828         perItemCount = dim;
829         d = 0;
830         cCnt = index;
831         break;
832     }
833     int pad = 20;
834     h = h-pad*1.5;
835     int res = h;
836
837     QPixmap pixmap(w*perItemCount + pad*2, h+pad*1.5);
838     /*
839     pixmap.fill(Qt::white);
840     */
841     QBitmap bitmap(w*perItemCount + pad*2, h+pad*1.5);
842     pixmap.setMask(bitmap);
843     pixmap.fill(Qt::transparent);
844     QPainter painter(&pixmap);
845
846     float minv = mins[d], maxv = maxes[d];
847     if(inputType == 1)
848     {
849         FOR(i, mins.size())
850         {
851             if(minv > mins[i]) minv = mins[i];
852             if(maxv < maxes[i]) maxv = maxes[i];
853         }
854     }
855
856     float edge = minv;
857     float delta = maxv - minv;
858
859     // we need to write down the scales
860     // first we need to find the closest decimal
861     int decimals = -log(delta)/log(10.) + (delta > 1 ? 0 : 1);
862     // now we know how "big" our data is: 10^(decimals), we can compute the closest boundaries
863     float powDecimals = pow(10., (double)(decimals+1));
864     float minsText = decimals ? int(minv*powDecimals)/powDecimals : minv;
865     float maxesText = decimals ? int(maxv*powDecimals)/powDecimals : maxv;
866     int minsPoint = (minsText-edge)/delta*res;
867     int maxesPoint = (maxesText-edge)/delta*res;
868
869     char format[255];
870     sprintf(format,"%%.%df", max(0,decimals+1));
871     QPoint firstTl(0,pad/2);
872
873     QFont font = painter.font();
874     font.setPointSize(9);
875     painter.setFont(font);
876     painter.setPen(QPen(Qt::black,1));
877     char text[255];
878     int axisSteps = 10;
879     int axisH = h / (axisSteps+1);
880     FOR(i, axisSteps+1)
881     {
882         float value = (maxesText - minsText)*i/(axisSteps) + minsText;
883         int y = h - ((maxesPoint-minsPoint)*i/(axisSteps) + minsPoint);
884         sprintf(text, format, value);
885         painter.drawText(firstTl.x(), firstTl.y() + y-axisH/2, pad*1.5-6, axisH, Qt::AlignVCenter | Qt::AlignRight, QString(text));
886         painter.drawLine(firstTl + QPointF(pad*1.5-3, y), firstTl + QPointF(pad*1.5, y));
887     }
888     painter.drawLine(firstTl.x() + pad*1.5, firstTl.y() + h - maxesPoint, firstTl.x() + pad*1.5, firstTl.y() + h - minsPoint);
889
890     // now we gather the data we need
891     vector<fvec> boxplots, densities, datas;
892     int classId = 0;
893     switch(inputType)
894     {
895     case 0: // dimension
896     {
897         int cCnt = 0;
898         for(map<int,vector<pair<fvec,fvec> > >::iterator it = classGraphData.begin(); it != classGraphData.end(); it++, cCnt++)
899         {
900             fvec boxplot = it->second[d].first;
901             fvec density = it->second[d].second;
902             boxplots.push_back(boxplot);
903             densities.push_back(density);
904             datas.push_back(dimClassData[d][it->first]);
905         }
906     }
907         break;
908     case 1: // classCount
909     {
910         int c=0;
911         for(map<int,vector<pair<fvec,fvec> > >::iterator it = classGraphData.begin(); it != classGraphData.end(); it++, c++)
912         {
913             if(c == cCnt)
914             {
915                 classId = it->first;
916                 FOR(d, dim)
917                 {
918                     fvec boxplot = it->second[d].first;
919                     fvec density = it->second[d].second;
920                     boxplots.push_back(boxplot);
921                     densities.push_back(density);
922                     datas.push_back(dimClassData[d][classId]);
923                 }
924                 break;
925             }
926         }
927     }
928         break;
929     }
930
931     // then we write the graph description/name
932     int graphCorner = pad*1.5;
933     int graphWidth = perItemCount*w;
934     font = painter.font();
935     font.setPointSize(12);
936     painter.setFont(font);
937     switch(inputType)
938     {
939     case 0:
940         painter.drawText(graphCorner, h+pad/2, graphWidth, pad, Qt::AlignHCenter|Qt::AlignCenter, ui->x1Combo->itemText(d));
941         break;
942     case 1:
943     {
944         QString className = QString("Class %1").arg(classId);
945         if(canvas->classNames.count(classId)) className = canvas->classNames[classId];
946         painter.drawText(graphCorner, h+pad/2, graphWidth, pad, Qt::AlignHCenter|Qt::AlignCenter, className);
947     }
948         break;
949     }
950
951     // then we draw the actual graphs
952     FOR(b, boxplots.size())
953     {
954         QPoint tl(pad + b*w,pad/2);
955         fvec boxplot = boxplots[b];
956         fvec density = densities[b];
957
958         int topPoint = h - (boxplot[0]-edge)/delta*res;
959         int highPoint = h - (boxplot[1]-edge)/delta*res;
960         int medPoint = h - (boxplot[2]-edge)/delta*res;
961         int lowPoint = h - (boxplot[3]-edge)/delta*res;
962         int bottomPoint = h - (boxplot[4]-edge)/delta*res;
963
964         float maxDensity = -FLT_MAX;
965         int maxDensityIndex = 0;
966         FOR(i, density.size())
967         {
968             if(maxDensity < density[i])
969             {
970                 maxDensity = density[i];
971                 maxDensityIndex = i;
972             }
973         }
974         QColor color;
975         if(ui->grayscaleCheck->isChecked())
976         {
977             switch(inputType)
978             {
979             case 0: // dimension
980                 color = QColor(cCnt*255/classCount,cCnt*255/classCount,cCnt*255/classCount);
981                 break;
982             case 1: // class
983                 color = QColor(d*255/dim,d*255/dim,d*255/dim);
984                 break;
985             }
986         }
987         else color = SampleColor[b%SampleColorCnt];
988
989         switch(type)
990         {
991         case 0: // Histograms
992         {
993             int meanPoint = (boxplot[5]-edge)/delta*res;
994             int sigmaPoint = boxplot[6]/delta*res;
995
996             painter.setPen(QPen(Qt::black,2));
997             painter.setBrush(color);
998             painter.drawRect(tl.x() + 20, tl.y() + h-meanPoint, 40, meanPoint);
999             painter.drawLine(tl.x() + 25, tl.y() + h-meanPoint+sigmaPoint, tl.x() + 55, tl.y() + h-meanPoint+sigmaPoint);
1000             painter.drawLine(tl.x() + 25, tl.y() + h-meanPoint-sigmaPoint, tl.x() + 55, tl.y() + h-meanPoint-sigmaPoint);
1001             painter.setPen(QPen(Qt::black,1));
1002             painter.drawLine(tl.x() + 40, tl.y() + h-meanPoint+sigmaPoint, tl.x() + 40, tl.y() + h-meanPoint-sigmaPoint);
1003         }
1004             break;
1005         case 1: // Box Plots
1006         {
1007             painter.setBrush(color);
1008             painter.setPen(QPen(Qt::black,2));
1009             painter.drawLine(tl + QPointF(15, bottomPoint),tl + QPointF(65, bottomPoint));
1010             painter.drawLine(tl + QPointF(15, topPoint),tl + QPointF(65, topPoint));
1011             painter.drawLine(tl + QPointF(40, bottomPoint),tl + QPointF(40, topPoint));
1012             painter.drawRect(tl.x() + 20, tl.y() + lowPoint, 40, highPoint - lowPoint);
1013             painter.setPen(QPen(Qt::black,1));
1014             painter.drawLine(tl + QPointF(20, medPoint),tl + QPointF(60, medPoint));
1015         }
1016             break;
1017         case 2: // HDR Box Plots
1018         {
1019             // we use the watershed algorithm to isolate separate regions
1020             fvec vals = density;
1021             sort(vals.begin(), vals.end());
1022             fvec water = density;
1023             fvec highWater, midWater, lowWater;
1024             FOR(i,vals.size())
1025             {
1026                 float v = vals[vals.size()-i-1];
1027                 if(!highWater.size() && v < maxDensity) highWater = water;
1028                 //if(!highWater.size() && v < 0.75*maxDensity) highWater = water;
1029                 if(!midWater.size() && v < 0.5*maxDensity) midWater = water;
1030                 if(!lowWater.size() && v < 0.25*maxDensity) lowWater = water;
1031                 FOR(j, water.size())
1032                 {
1033                     if(water[j] >= v) water[j] = -1;
1034                 }
1035             }
1036             // we need to create the segments
1037             fvec highSegments, midSegments, lowSegments;
1038             bool bHighOpen = false;
1039             bool bMidOpen = false;
1040             bool bLowOpen = false;
1041             FOR(i, water.size())
1042             {
1043                 if(highWater.size() && !bHighOpen && highWater[i] == -1)
1044                 {
1045                     bHighOpen = true;
1046                     highSegments.push_back(i);
1047                 }
1048                 else if(highWater.size() && bHighOpen && highWater[i] != -1)
1049                 {
1050                     bHighOpen = false;
1051                     highSegments.push_back(i);
1052                 }
1053                 if(midWater.size() && !bMidOpen && midWater[i] == -1)
1054                 {
1055                     bMidOpen = true;
1056                     midSegments.push_back(i);
1057                 }
1058                 else if(midWater.size() && bMidOpen && midWater[i] != -1)
1059                 {
1060                     bMidOpen = false;
1061                     midSegments.push_back(i);
1062                 }
1063                 if(lowWater.size() && !bLowOpen && lowWater[i] == -1)
1064                 {
1065                     bLowOpen = true;
1066                     lowSegments.push_back(i);
1067                 }
1068                 else if(lowWater.size() && bLowOpen && lowWater[i] != -1)
1069                 {
1070                     bLowOpen = false;
1071                     lowSegments.push_back(i);
1072                 }
1073             }
1074             if(bHighOpen) highSegments.push_back(water.size());
1075             if(bMidOpen) midSegments.push_back(water.size());
1076             if(bLowOpen) lowSegments.push_back(water.size());
1077
1078             QColor lightGray = color;
1079             QColor gray = QColor(color.red()*0.7, color.green()*0.7, color.blue()*0.7);
1080             QColor darkGray = QColor(color.red()*0.4, color.green()*0.4, color.blue()*0.4);
1081             painter.setPen(QPen(Qt::black,2));
1082             painter.setBrush(lightGray);
1083             painter.drawRect(tl.x() + 20, tl.y() + bottomPoint, 40, topPoint-bottomPoint);
1084             painter.setBrush(gray);
1085             painter.setPen(Qt::black);
1086
1087             /*
1088             FOR(i, lowSegments.size()/2)
1089             {
1090                 int yBottom = lowSegments[i*2]*(topPoint-bottomPoint)/(density.size()+1) + bottomPoint;
1091                 int yTop = lowSegments[i*2+1]*(topPoint-bottomPoint)/(density.size()+1) + bottomPoint;
1092                 painter.drawRect(tl.x() + 20, tl.y() + yBottom, 40, yTop-yBottom);
1093             }
1094             */
1095             //painter.setBrush(QColor(120,120,120));
1096             FOR(i, midSegments.size()/2)
1097             {
1098                 int yBottom = midSegments[i*2]*(topPoint-bottomPoint)/(density.size()+1) + bottomPoint;
1099                 int yTop = midSegments[i*2+1]*(topPoint-bottomPoint)/(density.size()+1) + bottomPoint;
1100                 painter.drawRect(tl.x() + 22, tl.y() + yBottom, 35, yTop-yBottom);
1101             }
1102             painter.setBrush(darkGray);
1103             FOR(i, highSegments.size()/2)
1104             {
1105                 int yBottom = highSegments[i*2]*(topPoint-bottomPoint)/(density.size()+1) + bottomPoint;
1106                 int yTop = highSegments[i*2+1]*(topPoint-bottomPoint)/(density.size()+1) + bottomPoint;
1107                 painter.drawRect(tl.x() + 22, tl.y() + yBottom, 35, yTop-yBottom);
1108             }
1109         }
1110             break;
1111         case 3: // Box Percentile Plots
1112         {
1113             int highPoint = -1;
1114             int midPoint = -1;
1115             int lowPoint = -1;
1116             float highX=0, midX=0, lowX=0;
1117
1118             painter.setRenderHint(QPainter::Antialiasing);
1119             painter.setPen(color);
1120             painter.setBrush(color);
1121             float oldX = 0, oldY = 0;
1122             float v = 0;
1123             int quartile = 0;
1124             FOR(i, density.size())
1125             {
1126                 float y = ((i+1)/(float)(density.size()+1))*(topPoint-bottomPoint) + bottomPoint;
1127                 if(quartile < 2) v += density[i];
1128                 else v -= density[i];
1129                 float x = v*w/2;
1130                 if(!i)
1131                 {
1132                     QPolygonF poly;
1133                     poly << tl + QPointF(40, bottomPoint)
1134                          << tl + QPointF(40+x, y)
1135                          << tl + QPointF(40-x, y);
1136                     painter.drawPolygon(poly);
1137                 }
1138                 else
1139                 {
1140                     if(fabs(oldY - y) < 1) painter.drawLine(tl + QPointF(40-x,y), tl + QPointF(40+x,y));
1141                     else
1142                     {
1143                         QPolygonF poly;
1144                         poly << tl + QPointF(40-x, y)
1145                              << tl + QPointF(40+x, y)
1146                              << tl + QPointF(40+oldX, oldY)
1147                              << tl + QPointF(40-oldX, oldY);
1148                         painter.drawPolygon(poly);
1149                     }
1150                 }
1151                 if(quartile == 0 && v > 0.25) quartile = 1;
1152                 if(quartile == 1 && v > 0.5) quartile = 2;
1153                 if(quartile == 2 && (v < 0.25 || v > 0.75)) quartile = 3;
1154                 oldX = x;
1155                 oldY = y;
1156             }
1157             painter.setPen(color);
1158             QPolygonF poly;
1159             poly << tl + QPointF(40+oldX, oldY)
1160                  << tl + QPointF(40-oldX, oldY)
1161                  << tl + QPointF(40, topPoint);
1162             painter.setBrush(color);
1163             painter.drawPolygon(poly);
1164
1165             painter.setPen(Qt::black);
1166             oldX = 0; oldY = 0; v = 0; quartile = 0;
1167             FOR(i, density.size())
1168             {
1169                 float y = ((i+1)/(float)(density.size()+1))*(topPoint-bottomPoint) + bottomPoint;
1170                 if(quartile < 2) v += density[i];
1171                 else v -= density[i];
1172                 float x = v*w/2;
1173                 if(!i)
1174                 {
1175                     painter.drawLine(tl + QPointF(40, bottomPoint), tl + QPointF(40-x, y));
1176                     painter.drawLine(tl + QPointF(40, bottomPoint), tl + QPointF(40+x, y));
1177                 }
1178                 else
1179                 {
1180                     painter.drawLine(tl + QPointF(40-oldX, oldY), tl + QPointF(40-x, y));
1181                     painter.drawLine(tl + QPointF(40+oldX, oldY), tl + QPointF(40+x, y));
1182                 }
1183                 if(quartile == 0 && v > 0.25)
1184                 {
1185                     quartile = 1;
1186                     highPoint = y;
1187                     highX = x;
1188                 }
1189                 if(quartile == 1 && v > 0.5)
1190                 {
1191                     quartile = 2;
1192                     midPoint = y;
1193                     midX = x;
1194                 }
1195                 if(quartile == 2 && (v < 0.25 || v > 0.75))
1196                 {
1197                     quartile = 3;
1198                     lowPoint = y;
1199                     lowX = x;
1200                 }
1201                 oldX = x;
1202                 oldY = y;
1203             }
1204
1205             painter.setPen(Qt::black);
1206             painter.drawLine(tl + QPointF(40-oldX, oldY), tl + QPointF(40, topPoint));
1207             painter.drawLine(tl + QPointF(40+oldX, oldY), tl + QPointF(40, topPoint));
1208             if(lowPoint != -1) painter.drawLine(tl + QPointF(40-lowX, lowPoint), tl + QPointF(40+lowX, lowPoint));
1209             if(midPoint != -1) painter.drawLine(tl + QPointF(40-midX, midPoint), tl + QPointF(40+midX, midPoint));
1210             if(highPoint != -1) painter.drawLine(tl + QPointF(40-highX, highPoint), tl + QPointF(40+highX, highPoint));
1211         }
1212             break;
1213         case 4: // Violin Plots
1214         {
1215             float oldX = 0, oldY = 0, v = 0;
1216             int quartile = 0;
1217
1218             painter.setRenderHint(QPainter::Antialiasing);
1219             painter.setPen(color);
1220             painter.setBrush(color);
1221             oldX = 0; oldY = 0; v = 0; quartile = 0;
1222             maxDensity = -FLT_MAX;
1223             FOR(i, density.size()) if(maxDensity < density[i]) maxDensity = density[i];
1224             FOR(i, density.size())
1225             {
1226                 float y = ((i+1)/(float)(density.size()+1))*(topPoint-bottomPoint) + bottomPoint;
1227                 float x = (density[i]/maxDensity)*(w/4);
1228                 if(!i)
1229                 {
1230                     QPolygonF poly;
1231                     poly << tl + QPointF(40, bottomPoint)
1232                          << tl + QPointF(40+x, y)
1233                          << tl + QPointF(40-x, y);
1234                     painter.drawPolygon(poly);
1235                 }
1236                 else
1237                 {
1238                     if(fabs(oldY - y) < 1) painter.drawLine(tl + QPointF(40-x,y), tl + QPointF(40+x,y));
1239                     else
1240                     {
1241                         QPolygonF poly;
1242                         poly << tl + QPointF(40-x, y)
1243                              << tl + QPointF(40+x, y)
1244                              << tl + QPointF(40+oldX, oldY)
1245                              << tl + QPointF(40-oldX, oldY);
1246                         painter.drawPolygon(poly);
1247                     }
1248                 }
1249                 oldX = x;
1250                 oldY = y;
1251             }
1252             painter.setPen(color);
1253             QPolygonF poly;
1254             poly << tl + QPointF(40+oldX, oldY)
1255                  << tl + QPointF(40-oldX, oldY)
1256                  << tl + QPointF(40, topPoint);
1257             painter.setBrush(color);
1258             painter.drawPolygon(poly);
1259
1260             painter.setPen(Qt::black);
1261             painter.setBrush(Qt::NoBrush);
1262             oldX = 0; oldY = 0; v = 0; quartile = 0;
1263             FOR(i, density.size())
1264             {
1265                 float y = ((i+1)/(float)(density.size()+1))*(topPoint-bottomPoint) + bottomPoint;
1266                 if(quartile < 2) v += density[i];
1267                 else v -= density[i];
1268                 float x = (density[i]/maxDensity)*w/4;
1269                 if(!i)
1270                 {
1271                     painter.drawLine(tl + QPointF(40, bottomPoint), tl + QPointF(40-x, y));
1272                     painter.drawLine(tl + QPointF(40, bottomPoint), tl + QPointF(40+x, y));
1273                 }
1274                 else
1275                 {
1276                     painter.drawLine(tl + QPointF(40-oldX, oldY), tl + QPointF(40-x, y));
1277                     painter.drawLine(tl + QPointF(40+oldX, oldY), tl + QPointF(40+x, y));
1278                 }
1279                 oldX = x;
1280                 oldY = y;
1281             }
1282             painter.drawLine(tl + QPointF(40-oldX, oldY), tl + QPointF(40, topPoint));
1283             painter.drawLine(tl + QPointF(40+oldX, oldY), tl + QPointF(40, topPoint));
1284
1285             painter.setBrush(Qt::gray);
1286             painter.drawLine(tl + QPointF(40, bottomPoint),tl + QPointF(40, topPoint));
1287             painter.drawRect(tl.x() + 38, tl.y() + lowPoint, 4, (highPoint-lowPoint));
1288             painter.drawLine(tl + QPointF(38, medPoint),tl + QPointF(42, medPoint));
1289         }
1290             break;
1291         case 5: // Raws
1292         {
1293             fvec data = datas[b];
1294             sort(data.begin(), data.end());
1295
1296             painter.setPen(Qt::black);
1297             painter.setBrush(color);
1298
1299             maxDensity = -FLT_MAX;
1300             FOR(i, density.size()) if(maxDensity < density[i]) maxDensity = density[i];
1301             int steps = density.size();
1302             ivec counts(steps, 0);
1303             FOR(i, data.size())
1304             {
1305                 int yIndex = (data[i]-edge)/delta*steps;
1306                 counts[yIndex]++;
1307             }
1308             int maxCounts = 0;
1309             FOR(i, counts.size()) if(maxCounts < counts[i]) maxCounts = counts[i];
1310             counts = ivec(steps, 0);
1311
1312             const float radius = 3;
1313             float flipFlop = 1.f;
1314             FOR(i, data.size())
1315             {
1316                 float y = (data[i]-edge)/delta*res;
1317                 int yIndex = (data[i]-edge)/delta*steps;
1318                 float x = 40 + (counts[yIndex]/float(maxCounts))*w*0.25*flipFlop;
1319                 painter.drawEllipse(QPointF(tl.x() + radius/2 + x, tl.y() +  h - y - radius/2), radius, radius);
1320                 counts[yIndex]++;
1321                 flipFlop *= -1.f;
1322             }
1323         }
1324             break;
1325         }
1326     }
1327     return pixmap;
1328 }
1329
1330 QPixmap Visualization::GetStarPixmap(int inputType, int dim, int classCount, int index, int w, int h,
1331                                      fvec mins, std::map<int,fvec> dimMins,
1332                                      fvec maxes, std::map<int,fvec> dimMaxes)
1333 {
1334     int d;
1335     int cCnt;
1336     switch(inputType)
1337     {
1338     case 0:
1339         d = index;
1340         cCnt = 0;
1341         break;
1342     case 1:
1343         d = 0;
1344         cCnt = index;
1345         break;
1346     }
1347     int pad = 20;
1348     w += pad;
1349     h = h-pad*1.5;
1350
1351     int classId = cCnt;
1352     int c=0;
1353     FORIT(dimMaxes, int, fvec)
1354     {
1355         if(c==cCnt)
1356         {
1357             classId = it->first;
1358             break;
1359         }
1360         c++;
1361     }
1362
1363     QPixmap pixmap(w, h+pad*1.5);
1364     QBitmap bitmap(w, h+pad*1.5);
1365     pixmap.setMask(bitmap);
1366     pixmap.fill(Qt::transparent);
1367     QPainter painter(&pixmap);
1368     painter.setRenderHint(QPainter::Antialiasing);
1369     painter.setPen(QPen(Qt::black, 2));
1370
1371     // then we write the graph description/name
1372     QFont font = painter.font();
1373     font.setPointSize(12);
1374     painter.setFont(font);
1375     switch(inputType)
1376     {
1377     case 0:
1378         painter.drawText(0, h+pad/2, w, pad, Qt::AlignHCenter|Qt::AlignCenter, ui->x1Combo->itemText(d));
1379         break;
1380     case 1:
1381     {
1382         QString className = QString("Class %1").arg(classId);
1383         if(canvas->classNames.count(classId)) className = canvas->classNames[classId];
1384         painter.drawText(0, h+pad/2, w, pad, Qt::AlignHCenter|Qt::AlignCenter, className);
1385     }
1386         break;
1387     }
1388
1389     fvec cmins(classCount,FLT_MAX), cmaxes(classCount,-FLT_MAX);
1390     fvec dMins, dMaxes;
1391     switch(inputType)
1392     {
1393     case 0:
1394     {
1395         int cnt=0;
1396         FORIT(dimMins, int, fvec)
1397         {
1398             dMins.push_back(dimMins[it->first][d]);
1399             dMaxes.push_back(dimMaxes[it->first][d]);
1400             if(cmins[cnt] > dimMins[it->first][d]) cmins[cnt] = dimMins[it->first][d];
1401             if(cmaxes[cnt] < dimMaxes[it->first][d]) cmaxes[cnt] = dimMaxes[it->first][d];
1402             cnt++;
1403         }
1404     }
1405         break;
1406     case 1:
1407         dMins = dimMins[classId];
1408         dMaxes = dimMaxes[classId];
1409         break;
1410     }
1411     float minminv=FLT_MAX, maxmaxv=-FLT_MAX;
1412     FOR(i, mins.size())
1413     {
1414         if(minminv > mins[i]) minminv = mins[i];
1415         if(maxmaxv < maxes[i]) maxmaxv = maxes[i];
1416     }
1417
1418     int maxRadius = min(w,h)*0.4;
1419     QPointF center(w/2, h/2 + pad/2);
1420     painter.setBrush(Qt::NoBrush);
1421     FOR(d, dMins.size())
1422     {
1423         float minv, maxv;
1424         switch(inputType)
1425         {
1426         case 0:
1427             minv = (dMins[d]-minminv)/(maxmaxv-minminv);
1428             maxv = (dMaxes[d]-minminv)/(maxmaxv-minminv);
1429             //minv = (dMins[d]-cmins[d])/(cmaxes[d]-cmins[d]);
1430             //maxv = (dMaxes[d]-cmins[d])/(cmaxes[d]-cmins[d]);
1431             break;
1432         case 1:
1433             minv = (dMins[d]-mins[d])/(maxes[d]-mins[d]);
1434             maxv = (dMaxes[d]-mins[d])/(maxes[d]-mins[d]);
1435             break;
1436         }
1437
1438         // we now draw the arcs in 16th of degree
1439         float angleStart = (360*16)*d/dMins.size();
1440         float angleLen = (360*16)/dMins.size();
1441         float maxR = maxRadius * maxv;
1442         float minR = maxRadius * minv;
1443         QColor color = SampleColor[d%SampleColorCnt];
1444         float ratio = 0.1;
1445         QColor lighterColor(color.red()*ratio + 255*(1-ratio),
1446                             color.green()*ratio + 255*(1-ratio),
1447                             color.blue()*ratio + 255*(1-ratio));
1448         painter.setBrush(color);
1449         painter.drawPie(QRectF(center-QPointF(maxR,maxR), center+QPoint(maxR,maxR)),angleStart, angleLen);
1450         painter.setBrush(lighterColor);
1451         painter.drawPie(QRectF(center-QPointF(minR,minR), center+QPoint(minR,minR)),angleStart, angleLen);
1452     }
1453
1454     return pixmap;
1455 }
1456
1457 fvec Visualization::BoxPlot(fvec data)
1458 {
1459     fvec boxplot(7);
1460     if(!data.size()) return boxplot;
1461     int pad = -16;
1462     int nanCount = 0;
1463     FOR(i, data.size()) if(data[i] != data[i]) nanCount++;
1464
1465     float mean = 0;
1466     float sigma = 0;
1467     FOR(i, data.size()) if(data[i]==data[i]) mean += data[i] / (data.size()-nanCount);
1468     FOR(i, data.size()) if(data[i]==data[i]) sigma += powf(data[i]-mean,2);
1469     sigma = sqrtf(sigma/(data.size()-nanCount));
1470
1471     float top, bottom, median, quartLow, quartHi;
1472     vector<float> outliers;
1473     vector<float> sorted;
1474
1475     if(data.size() > 1)
1476     {
1477         if(sigma==0)
1478         {
1479             sorted = data;
1480         }
1481         else
1482         {
1483             // we look for outliers using the 3*sigma rule
1484             FOR(i, data.size())
1485             {
1486                 if(data[i]!=data[i]) continue;
1487                 if (data[i] - mean < 3*sigma)
1488                     sorted.push_back(data[i]);
1489                 else outliers.push_back(data[i]);
1490             }
1491         }
1492         if(!sorted.size()) return boxplot;
1493         sort(sorted.begin(), sorted.end());
1494         int count = sorted.size();
1495         int half = count/2;
1496         bottom = sorted[0];
1497         top = sorted[sorted.size()-1];
1498
1499         median = count%2 ? sorted[half] : (sorted[half] + sorted[half - 1])/2;
1500         if(count < 4)
1501         {
1502             quartLow = bottom;
1503             quartHi = top;
1504         }
1505         else
1506         {
1507             quartLow = half%2 ? sorted[half/2] : (sorted[half/2] + sorted[half/2 - 1])/2;
1508             quartHi = half%2 ? sorted[half*3/2] : (sorted[half*3/2] + sorted[half*3/2 - 1])/2;
1509         }
1510     }
1511     else
1512     {
1513         mean = top = bottom = median = quartLow = quartHi = data[0];
1514         sigma = 0;
1515     }
1516     boxplot[0] = top;
1517     boxplot[1] = quartHi;
1518     boxplot[2] = median;
1519     boxplot[3] = quartLow;
1520     boxplot[4] = bottom;
1521     boxplot[5] = mean;
1522     boxplot[6] = sigma;
1523     return boxplot;
1524 }
1525
1526 fvec Visualization::Density(fvec data, float minv, float maxv, int bins)
1527 {
1528     fvec density(bins,0);
1529     if(!data.size()) return density;
1530     const int count = data.size();
1531     FOR(i, data.size())
1532     {
1533         float v = data[i];
1534         int index = (v-minv)/(maxv-minv)*bins;
1535         index = min(bins-1,max(0,index));
1536         density[index] += 1.f/count;
1537     }
1538     return density;
1539 }