- Integrated the file import into the main interface (not as a plugin anymore)
[mldemos:mldemos.git] / Core / expose.cpp
1 #include <public.h>
2 #include <mymaths.h>
3 #include <basicMath.h>
4 #include "expose.h"
5 #include "ui_expose.h"
6 #include <QClipboard>
7 #include <QBitmap>
8 #include <QDebug>
9
10 using namespace std;
11
12 Expose::Expose(Canvas *canvas, QWidget *parent) :
13     QWidget(parent),
14     ui(new Ui::Expose),
15     canvas(canvas)
16 {
17     ui->setupUi(this);
18     connect(ui->typeCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(Repaint()));
19     connect(ui->clipboardButton, SIGNAL(clicked()), this, SLOT(Clipboard()));
20     this->setWindowTitle("Multivariate Visualisation");
21 }
22
23 Expose::~Expose()
24 {
25     delete ui;
26 }
27
28 void Expose::DrawData(QPixmap& pixmap, std::vector<fvec> samples, ivec labels, std::vector<dsmFlags> flags, int type, bool bProjected, QStringList names, std::pair<fvec,fvec> bounds)
29 {
30     if(!samples.size() || !labels.size()) return;
31     vector<QColor> sampleColors(labels.size());
32     FOR(i, labels.size())
33     {
34         QColor color = SampleColor[labels[i]%SampleColorCnt];
35         sampleColors[i] = color;
36     }
37     DrawData(pixmap, samples, sampleColors, flags, type, bProjected, false, names, bounds);
38 }
39
40 void Expose::DrawData(QPixmap& pixmap, std::vector<fvec> samples, std::vector<QColor> sampleColors, std::vector<dsmFlags> flags, int type, bool bProjected, bool bLearned, QStringList names, std::pair<fvec,fvec> bounds)
41 {
42     if(!samples.size()) return;
43     int w = pixmap.width(), h = pixmap.height();
44
45     int dim = samples[0].size();
46     int gridX = dim;
47     int gridY = dim;
48
49     fvec mins = bounds.first;
50     fvec maxes = bounds.second;
51     if(!bounds.first.size())
52     {
53         mins.resize(dim, FLT_MAX);
54         maxes.resize(dim, -FLT_MIN);
55         FOR(d, dim)
56         {
57             FOR(i, samples.size())
58             {
59                 mins[d] = min(mins[d], samples[i][d]);
60                 maxes[d] = max(maxes[d], samples[i][d]);
61             }
62         }
63         bounds.first = mins;
64         bounds.second = maxes;
65     }
66     fvec diffs(dim, 0);
67     FOR(d, dim)
68         {
69                 diffs[d] = maxes[d] - mins[d];
70         }
71
72     int pad = 20;
73     int mapW = w - pad*2, mapH = h - pad*2;
74     QPainter painter(&pixmap);
75     painter.setRenderHint(QPainter::Antialiasing);
76
77     switch(type)
78     {
79     case 0: // scatterplots
80     {
81         mapW = w/gridX - pad*2;
82         mapH = h/gridX - pad*2;
83         int radiusBase = max(5.f, 5 * sqrtf(mapW / 200.f));
84
85         QList<QPixmap> maps;
86         FOR(index0, dim)
87         {
88             FOR(index1, dim)
89                         {
90                 QPixmap map(mapW + 2*pad,mapH + 2*pad);
91                 int smallW = map.width() - 2*pad, smallH = map.height() - 2*pad;
92                 if(!bLearned) map.fill(Qt::white);
93                 else
94                 {
95                     QBitmap bitmap(map.size());
96                     bitmap.clear();
97                     map.setMask(bitmap);
98                     map.fill(Qt::transparent);
99                 }
100                 QPainter painter(&map);
101                 painter.setRenderHint(QPainter::Antialiasing);
102
103                                 if(diffs[index0] != 0.f && diffs[index1] != 0.f)
104                                 {
105                                         FOR(i, samples.size())
106                                         {
107                         if(flags[i] == _TRAJ || flags[i] == _OBST) continue;
108                                                 float x = samples[i][index0];
109                                                 float y = samples[i][index1];
110                                                 x = (x-mins[index0])/diffs[index0];
111                                                 y = (y-mins[index1])/diffs[index1];
112                                                 QPointF point(y*smallW + pad, x*smallH + pad);
113                         float radius = radiusBase;
114                         if(bLearned)
115                         {
116                             radius = radiusBase + radiusBase/2;
117                             if(i < sampleColors.size()) painter.setPen(QPen(sampleColors[i], radiusBase/2));
118                             painter.setBrush(Qt::NoBrush);
119                             painter.drawEllipse(QRectF(point.x()-radius/2.,point.y()-radius/2.,radius,radius));
120                             radius += radiusBase/2-1;
121                             painter.setPen(QPen(Qt::black, 0.5));
122                             painter.drawEllipse(QRectF(point.x()-radius/2.,point.y()-radius/2.,radius,radius));
123                         }
124                         else
125                         {
126                             if(i < sampleColors.size()) painter.setBrush(sampleColors[i]);
127                             painter.setPen(Qt::black);
128                             painter.drawEllipse(QRectF(point.x()-radius/2.,point.y()-radius/2.,radius,radius));
129                         }
130                                         }
131                 }
132                 painter.setBrush(Qt::NoBrush);
133                 painter.setPen(Qt::black);
134                 painter.setRenderHint(QPainter::Antialiasing, false);
135                 painter.drawRect(pad/2,pad/2,smallW+pad, smallH+pad);
136                 QString text =  QString("x%1  x%2").arg(index1+1).arg(index0+1);
137                 if(index0 < names.size() && index1 < names.size()) text = names.at(index1) + " " + names.at(index0);
138                 if(bProjected) text = QString("e%1  e%2").arg(index1+1).arg(index0+1);
139                 painter.drawText(pad/2+1, map.height()-pad/2-1,text);
140                 maps.push_back(map);
141             }
142         }
143
144         FOR(i, maps.size())
145         {
146             int xIndex = i%gridX;
147             int yIndex = i/gridX;
148             painter.drawPixmap(QPoint(xIndex*w/gridX, yIndex*h/gridY), maps[i]);
149         }
150     }
151         break;
152     case 1: // parallel coordinates
153     {
154         painter.setRenderHint(QPainter::Antialiasing, false);
155         FOR(d, dim)
156         {
157             float x = d*mapW/(float)(dim-1) + pad;
158             painter.setPen(Qt::black);
159             painter.drawLine(x, pad, x, mapH+pad);
160             QString text =  QString("x%1").arg(d+1);
161             if(d < names.size()) text = names.at(d);
162             if(bProjected) text = QString("e%1").arg(d+1);
163             painter.drawText(x-10, mapH+2*pad-4, text);
164         }
165
166         painter.setRenderHint(QPainter::Antialiasing);
167         FOR(i, samples.size())
168         {
169             QPointF old;
170                         FOR(d, dim)
171             {
172                                 if(diffs[d] == 0.f) continue;
173                                 float x = d*mapW/(float)(dim-1) + pad;
174                 float y = samples[i][d];
175                 y = (y-mins[d])/(maxes[d] - mins[d]);
176                 QPointF point(x,pad + y*mapH);
177                 float radius = 7;
178                 QColor color = Qt::black;
179                 if(i < sampleColors.size()) color = sampleColors[i];
180                 painter.setBrush(color);
181                 painter.setPen(Qt::black);
182                 painter.drawEllipse(QRectF(point.x()-radius/2.,point.y()-radius/2.,radius,radius));
183                 if(color == Qt::white) color = Qt::black;
184                 painter.setPen(color);
185                 if(d) painter.drawLine(point, old);
186                 old = point;
187             }
188         }
189     }
190         break;
191     case 2: // radial graphs
192     {
193         float radius = min(mapW, mapH)/3.f;
194         QPointF center(mapW*0.5f, mapH*0.5f);
195         QPointF old;
196         painter.setPen(Qt::black);
197         painter.setBrush(Qt::NoBrush);
198         painter.drawEllipse(center, radius, radius);
199         FOR(d, dim)
200         {
201                         float theta = d/(float)(dim)*2*M_PI;
202             QPointF point = QPointF(cos(theta), sin(theta))*radius;
203             painter.setBrush(Qt::white);
204             painter.drawEllipse(center+point, 4, 4);
205             QString text =  QString("x%1").arg(d+1);
206             if(d < names.size()) text = names.at(d);
207             if(bProjected) text = QString("e%1").arg(d+1);
208             painter.drawText(center + point*1.1, text);
209             old = point;
210         }
211
212         painter.setPen(Qt::black);
213         FOR(i, samples.size())
214         {
215             QPointF samplePoint;
216             float dimSum = 0;
217             FOR(d, dim)
218             {
219                                 if(diffs[d] == 0.f) continue;
220                                 float theta = d/(float)(dim)*2*M_PI;
221                 QPointF point = QPointF(cos(theta), sin(theta))*radius;
222                 float value = (samples[i][d]-mins[d])/(maxes[d]-mins[d]);
223                 samplePoint += point*value;
224                 dimSum += value;
225             }
226             samplePoint /= dimSum;
227             float drawRadius = 7;
228             QPointF point = center + samplePoint;
229             QColor color = Qt::black;
230             if(i < sampleColors.size()) color = sampleColors[i];
231             painter.setBrush(color);
232             painter.drawEllipse(QRectF(point.x()-drawRadius/2.,point.y()-drawRadius/2.,drawRadius,drawRadius));
233         }
234     }
235         break;
236     case 3: // andrews plots
237     {
238         float radius = min(mapW, mapH)/3.f;
239         QPointF center(mapW*0.5f, mapH*0.5f);
240         painter.setPen(Qt::black);
241
242         // 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)
243         vector<fvec> values(samples.size());
244         const int steps = 200;
245         float minv=FLT_MAX, maxv=-FLT_MAX;
246         FOR(i, samples.size())
247         {
248             values[i].resize(steps);
249             FOR(j, steps)
250             {
251                 float t = j/(float)steps*(M_PI*2) - M_PI;
252                 float value = 0;
253                 FOR(d, dim)
254                 {
255                                         if(diffs[d] == 0.f) continue;
256                                         float v = (samples[i][d]-mins[d])/(maxes[d]-mins[d]);
257                     if(!d) value += v*sqrtf(2.f);
258                     else
259                     {
260
261                         value += v * (d%2 ? sin(t*((d+1)/2)) : cos(t*((d+1)/2)));
262                     }
263                 }
264                 values[i][j] = value;
265                 minv = min(minv, value);
266                 maxv = max(maxv, value);
267             }
268         }
269
270         painter.setRenderHint(QPainter::Antialiasing, false);
271         painter.setPen(Qt::black);
272         painter.drawLine(pad, mapH-10+pad, mapW+pad, mapH-10+pad);
273         QFont font = painter.font();
274         font.setPointSize(9);
275         painter.setFont(font);
276         QStringList ticks;
277         ticks << "-pi" << "-pi/2" << "0" << "pi/2" << "pi";
278         FOR(i, 5)
279         {
280             int x = i*mapW/(4);
281             painter.setRenderHint(QPainter::Antialiasing, false);
282             painter.drawLine(pad + x, mapH-10 - 4 + pad, pad + x, mapH-10 + 4 + pad);
283             painter.setRenderHint(QPainter::Antialiasing);
284             painter.drawText(pad + x-25,mapH-10 + 4 + pad, 50, 20, Qt::AlignCenter, ticks[i]);
285         }
286
287         painter.setRenderHint(QPainter::Antialiasing);
288         FOR(i, values.size())
289         {
290             QColor color = Qt::black;
291             if(i < sampleColors.size()) color = sampleColors[i];
292             if(color == Qt::white) color = Qt::black;
293             painter.setPen(QPen(color,0.5));
294
295             QPointF old;
296             FOR(j, values[i].size())
297             {
298                 float value = (values[i][j]-minv)/(maxv-minv);
299                 QPointF point = QPointF(j*pixmap.width()/steps, value*mapH + pad);
300                 if(j) painter.drawLine(point, old);
301                 old = point;
302             }
303         }
304     }
305         break;
306     }
307 }
308
309 void Expose::DrawTrajectories(QPixmap& pixmap, vector< vector<fvec> > trajectories, ivec labels, int type, int drawMode, std::pair<fvec,fvec> bounds)
310 {
311     if(!trajectories.size() || !labels.size()) return;
312     vector<QColor> sampleColors(labels.size());
313     FOR(i, labels.size())
314     {
315         QColor color = SampleColor[labels[i]%SampleColorCnt];
316         sampleColors[i] = color;
317     }
318     DrawTrajectories(pixmap, trajectories, sampleColors, type, drawMode, bounds);
319 }
320
321 void Expose::DrawTrajectories(QPixmap& pixmap, vector< vector<fvec> > trajectories, std::vector<QColor> sampleColors, int type, int drawMode, std::pair<fvec,fvec> bounds)
322 {
323     if(!trajectories.size()) return;
324     int w = pixmap.width(), h = pixmap.height();
325
326     int dim = trajectories[0][0].size()/2; // trajectories have velocity embedded so we only want half the dimensions
327     if(!sampleColors.size()) dim = trajectories[0][0].size(); // unless we are actually drawing the trajectories without velocity
328     int gridX = dim;
329     int gridY = dim;
330
331     fvec mins = bounds.first;
332     fvec maxes = bounds.second;
333     if(!bounds.first.size())
334     {
335         mins.resize(dim, FLT_MAX);
336         maxes.resize(dim, -FLT_MIN);
337         FOR(d, dim)
338         {
339             FOR(i, trajectories.size())
340             {
341                 FOR(j, trajectories[i].size())
342                 {
343                     mins[d] = min(mins[d], trajectories[i][j][d]);
344                     maxes[d] = max(maxes[d], trajectories[i][j][d]);
345                 }
346             }
347         }
348         bounds.first = mins;
349         bounds.second = maxes;
350     }
351     fvec diffs(dim, 0);
352     FOR(d, dim)
353     {
354         diffs[d] = maxes[d] - mins[d];
355     }
356
357     int pad = 20;
358     int mapW = w - pad*2, mapH = h - pad*2;
359     QPainter painter(&pixmap);
360     painter.setRenderHint(QPainter::Antialiasing);
361
362     if(type==0)
363     {
364         mapW = w/gridX - pad*2;
365         mapH = h/gridX - pad*2;
366         int radiusBase = max(4.f, 4 * sqrtf(mapW / 200.f));
367
368         QList<QPixmap> maps;
369         FOR(index0, dim)
370         {
371             FOR(index1, dim)
372             {
373                 QPixmap map(mapW + 2*pad,mapH + 2*pad);
374                 int smallW = map.width() - 2*pad, smallH = map.height() - 2*pad;
375
376                 QBitmap bitmap(map.size());
377                 bitmap.clear();
378                 map.setMask(bitmap);
379                 map.fill(Qt::transparent);
380
381                 QPainter painter(&map);
382                 painter.setRenderHint(QPainter::Antialiasing);
383
384                 if(diffs[index0] != 0.f && diffs[index1] != 0.f)
385                 {
386                     int sampleColorCounter = 0;
387                     FOR(i, trajectories.size())
388                     {
389                         QPointF oldPoint, firstPoint, point;
390                         int count = trajectories[i].size();
391                         if(drawMode == 0 && i < sampleColors.size()) painter.setBrush(sampleColors[sampleColorCounter]);
392                         else painter.setBrush(Qt::black);
393                         FOR(j, count)
394                         {
395                             fvec pt = trajectories[i][j];
396                             float x = pt[index0];
397                             float y = pt[index1];
398                             x = (x-mins[index0])/diffs[index0];
399                             y = (y-mins[index1])/diffs[index1];
400                             point = QPointF(y*smallW + pad, x*smallH + pad);
401                             if(drawMode == 0) painter.setPen(QPen(Qt::black, 0.5));
402                             else if(drawMode == 1) painter.setPen(QPen(Qt::green, 1));
403                             if(j)
404                             {
405                                 painter.drawLine(point, oldPoint);
406                                 if(j<count-1 && sampleColors.size())
407                                 {
408                                     painter.drawEllipse(point, max(1,radiusBase/4), max(1,radiusBase/4));
409                                 }
410                             }
411                             else firstPoint = point;
412                             oldPoint = point;
413                             sampleColorCounter++;
414                         }
415                         if(drawMode == 0)
416                         {
417                             painter.setBrush(Qt::NoBrush);
418                             painter.setPen(Qt::green);
419                             painter.drawEllipse(firstPoint, radiusBase, radiusBase);
420                             painter.setPen(Qt::red);
421                             painter.drawEllipse(point, radiusBase/2, radiusBase/2);
422                         }
423                     }
424                 }
425                 /*
426                 painter.setBrush(Qt::NoBrush);
427                 painter.setPen(Qt::black);
428                 painter.setRenderHint(QPainter::Antialiasing, false);
429                 painter.drawRect(pad/2,pad/2,smallW+pad, smallH+pad);
430                 QString text =  QString("x%1  x%2").arg(index1+1).arg(index0+1);
431                 if(index0 < names.size() && index1 < names.size()) text = names.at(index1) + " " + names.at(index0);
432                 if(bProjected) text = QString("e%1  e%2").arg(index1+1).arg(index0+1);
433                 painter.drawText(pad/2+1, map.height()-pad/2-1,text);
434                 */
435                 maps.push_back(map);
436             }
437         }
438
439         FOR(i, maps.size())
440         {
441             int xIndex = i%gridX;
442             int yIndex = i/gridX;
443             painter.drawPixmap(QPoint(xIndex*w/gridX, yIndex*h/gridY), maps[i]);
444         }
445     }
446 }
447
448 void Expose::DrawVariableData(QPixmap& pixmap, std::vector<fvec> samples, ivec labels, int type, fvec params, bool bProjected, QStringList names)
449 {
450     if(!samples.size() || !labels.size()) return;
451     vector<QColor> sampleColors(labels.size());
452     FOR(i, labels.size())
453     {
454         QColor color = SampleColor[labels[i]%SampleColorCnt];
455         sampleColors[i] = color;
456     }
457     DrawVariableData(pixmap, samples, sampleColors, type, params, bProjected, false, names);
458 }
459
460 void Expose::DrawVariableData(QPixmap& pixmap, std::vector<fvec> samples, std::vector<QColor> sampleColors, int type, fvec params, bool bProjected, bool bLearned, QStringList names)
461 {
462     if(!samples.size()) return;
463     int w = pixmap.width(), h = pixmap.height();
464
465     int dim = samples[0].size();
466     int gridX = dim;
467     int gridY = dim;
468
469     fvec mins(dim, FLT_MAX), maxes(dim, -FLT_MIN), diffs(dim, 0);
470     FOR(d, dim)
471     {
472         FOR(i, samples.size())
473         {
474             mins[d] = min(mins[d], samples[i][d]);
475             maxes[d] = max(maxes[d], samples[i][d]);
476         }
477     }
478     FOR(d, dim)
479     {
480         diffs[d] = maxes[d] - mins[d];
481     }
482
483     int pad = 20;
484     int mapW = w - pad*2, mapH = h - pad*2;
485     QPainter painter(&pixmap);
486     painter.setRenderHint(QPainter::Antialiasing);
487
488     painter.setPen(Qt::black);
489     switch(type)
490     {
491     case 0: // bubble graph
492     {
493         painter.setRenderHint(QPainter::Antialiasing, false);
494         painter.drawLine(pad, mapH+pad, mapW+pad, mapH+pad);
495         painter.drawLine(pad, pad, pad, mapH+pad);
496
497         int xIndex = params[0];
498         int yIndex = params[1];
499         int sIndex = params[2];
500         if(sIndex == -1)
501         {
502             srand48(0);
503             srand(0);
504         }
505         painter.setRenderHint(QPainter::Antialiasing);
506         FOR(i, samples.size())
507         {
508             float x = (samples[i][xIndex]-mins[xIndex])/(diffs[xIndex]);
509             float y = (samples[i][yIndex]-mins[yIndex])/(diffs[yIndex]);
510             QPointF point(x*mapW + pad,y*mapH + pad);
511
512             float radius = 10;
513             if(sIndex != -1)
514             {
515                 radius = (samples[i][sIndex]-mins[sIndex])/(diffs[sIndex]);
516                 radius = radius*60 + 3;
517             }
518             else
519             {
520                 radius = drand48()*40 + 3;
521             }
522
523             QColor color = Qt::black;
524             if(i < sampleColors.size()) color = sampleColors[i];
525             painter.setBrush(color);
526             painter.setPen(Qt::black);
527             painter.setOpacity(0.5f);
528             painter.drawEllipse(QRectF(point.x()-radius/2.,point.y()-radius/2.,radius,radius));
529         }
530     }
531     break;
532     }
533 }
534 void Expose::GenerateAndrewsPlots()
535 {
536     std::vector<fvec> samples = canvas->data->GetSamples();
537     ivec labels = canvas->data->GetLabels();
538     if(!samples.size()) return;
539     int dim = samples[0].size();
540
541     fvec mins(dim, FLT_MAX), maxes(dim, -FLT_MIN);
542     FOR(d, dim)
543     {
544         FOR(i, samples.size())
545         {
546             mins[d] = min(mins[d], samples[i][d]);
547             maxes[d] = max(maxes[d], samples[i][d]);
548         }
549     }
550
551     int pad = 20;
552     int mapW = (ui->scrollArea->width()-12) - pad*2, mapH = (ui->scrollArea->height()-12) - pad*2;
553
554     ui->scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
555     ui->scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
556     pixmap = QPixmap(ui->scrollArea->width(), ui->scrollArea->height());
557     pixmap.fill(Qt::white);
558     QPainter painter(&pixmap);
559     painter.setRenderHint(QPainter::Antialiasing);
560     float radius = min(mapW, mapH)/3.f;
561     QPointF center(mapW*0.5f, mapH*0.5f);
562     QPointF old;
563     painter.setPen(Qt::black);
564
565     // 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)
566     vector<fvec> values(samples.size());
567     const int steps = 200;
568     float minv=FLT_MAX, maxv=-FLT_MAX;
569     FOR(i, samples.size())
570     {
571         values[i].resize(steps);
572         FOR(j, steps)
573         {
574             float t = j/(float)steps*(M_PI*2) - M_PI;
575             float value = 0;
576             FOR(d, dim)
577             {
578                 float v = (samples[i][d]-mins[d])/(maxes[d]-mins[d]);
579                 if(!d) value += v*sqrtf(2.f);
580                 else
581                 {
582
583                     value += v * (d%2 ? sin(t*((d+1)/2)) : cos(t*((d+1)/2)));
584                 }
585             }
586             values[i][j] = value;
587             minv = min(minv, value);
588             maxv = max(maxv, value);
589         }
590     }
591
592     FOR(i, values.size())
593     {
594         FOR(j, values[i].size())
595         {
596             float value = (values[i][j]-minv)/(maxv-minv);
597             QPointF point = QPointF(j*pixmap.width()/steps, value*mapH + pad);
598             QColor color = SampleColor[labels[i]%SampleColorCnt];
599                         painter.setPen(QPen(color,0.5));
600                         if(j) painter.drawLine(point, old);
601             old = point;
602         }
603     }
604
605     ui->display->setPixmap(pixmap);
606     ui->display->repaint();
607 }
608
609
610 void Expose::GenerateRadialGraph()
611 {
612     std::vector<fvec> samples = canvas->data->GetSamples();
613     ivec labels = canvas->data->GetLabels();
614     if(!samples.size()) return;
615     int dim = samples[0].size();
616
617     fvec mins(dim, FLT_MAX), maxes(dim, -FLT_MIN);
618     FOR(d, dim)
619     {
620         FOR(i, samples.size())
621         {
622             mins[d] = min(mins[d], samples[i][d]);
623             maxes[d] = max(maxes[d], samples[i][d]);
624         }
625     }
626
627     int pad = 20;
628     int mapW = (ui->scrollArea->width()-12) - pad*2, mapH = (ui->scrollArea->height()-12) - pad*2;
629
630     ui->scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
631     ui->scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
632     pixmap = QPixmap(ui->scrollArea->width(), ui->scrollArea->height());
633     pixmap.fill(Qt::white);
634     QPainter painter(&pixmap);
635     painter.setRenderHint(QPainter::Antialiasing);
636     float radius = min(mapW, mapH)/3.f;
637     QPointF center(mapW*0.5f, mapH*0.5f);
638     QPointF old;
639     painter.setPen(Qt::black);
640     FOR(d, dim)
641     {
642         float theta = d/(float)(dim)*2*M_PI;
643         QPointF point = QPointF(cos(theta), sin(theta))*radius;
644         if(d) painter.drawLine(center + point, center + old);
645         painter.drawText(center + point*1.1, QString("e%1").arg(d+1));
646         old = point;
647     }
648     painter.drawLine(center + QPointF(1.f, 0.f)*radius, center + old);
649
650     painter.setRenderHint(QPainter::Antialiasing);
651     FOR(i, samples.size())
652     {
653         QPointF samplePoint;
654         float dimSum = 0;
655         FOR(d, dim)
656         {
657             float theta = d/(float)(dim)*2*M_PI;
658             QPointF point = QPointF(cos(theta), sin(theta))*radius;
659             float value = (samples[i][d]-mins[d])/(maxes[d]-mins[d]);
660             samplePoint += point*value;
661             dimSum += value;
662         }
663         samplePoint /= dimSum;
664         float drawRadius = 7;
665         Canvas::drawSample(painter, center + samplePoint, drawRadius, labels[i]);
666         QColor color = SampleColor[labels[i]%SampleColorCnt];
667         painter.setPen(color);
668     }
669
670     ui->display->setPixmap(pixmap);
671     ui->display->repaint();
672 }
673
674 void Expose::GenerateParallelCoords()
675 {
676     std::vector<fvec> samples = canvas->data->GetSamples();
677     ivec labels = canvas->data->GetLabels();
678     if(!samples.size()) return;
679     int dim = samples[0].size();
680
681     fvec mins(dim, FLT_MAX), maxes(dim, -FLT_MIN);
682     FOR(d, dim)
683     {
684         FOR(i, samples.size())
685         {
686             mins[d] = min(mins[d], samples[i][d]);
687             maxes[d] = max(maxes[d], samples[i][d]);
688         }
689     }
690
691     int pad = 20;
692     int mapW = (ui->scrollArea->width()-12) - pad*2, mapH = (ui->scrollArea->height()-12) - pad*2;
693
694     ui->scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
695     ui->scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
696     pixmap = QPixmap(ui->scrollArea->width(), ui->scrollArea->height());
697     pixmap.fill(Qt::white);
698     QPainter painter(&pixmap);
699     FOR(d, dim)
700     {
701         float x = d*mapW/(float)(dim-1) + pad;
702         painter.setPen(Qt::black);
703         painter.drawLine(x, pad, x, mapH+pad);
704         painter.drawText(x-10, mapH+2*pad-4, QString("e%1").arg(d+1));
705     }
706
707     painter.setRenderHint(QPainter::Antialiasing);
708     FOR(i, samples.size())
709     {
710         QPointF old;
711         FOR(d, dim)
712         {
713             float x = d*mapW/(float)(dim-1) + pad;
714             float y = samples[i][d];
715             y = (y-mins[d])/(maxes[d] - mins[d]);
716             QPointF point(x,pad + y*mapH);
717             float radius = 7;
718             Canvas::drawSample(painter, point, radius, labels[i]);
719             QColor color = SampleColor[labels[i]%SampleColorCnt];
720             painter.setPen(color);
721             if(d) painter.drawLine(point, old);
722             old = point;
723         }
724     }
725
726     ui->display->setPixmap(pixmap);
727     ui->display->repaint();
728 }
729
730 void Expose::GenerateScatterPlot(bool bCheckOnly)
731 {
732     std::vector<fvec> samples = canvas->data->GetSamples();
733     ivec labels = canvas->data->GetLabels();
734     if(!samples.size()) return;
735     int dim = samples[0].size();
736     bool bEvenCount = dim%2 == 1;
737     int gridX = dim;
738     int gridY = dim;
739
740     fvec mins(dim, FLT_MAX), maxes(dim, -FLT_MIN);
741     FOR(d, dim)
742     {
743         FOR(i, samples.size())
744         {
745             mins[d] = min(mins[d], samples[i][d]);
746             maxes[d] = max(maxes[d], samples[i][d]);
747         }
748     }
749
750     int pad = 20;
751     int mapW = (ui->scrollArea->width()-12)/gridX - pad*2;
752     int mapH = (ui->scrollArea->height()-12)/gridX - pad*2;
753
754     bool bScroll = false;
755     if(mapW < 100 || mapH < 100)
756     {
757         bScroll = true;
758         mapW = max(mapW, 100);
759         mapH = max(mapH, 100);
760     }
761     if(bScroll && bCheckOnly) return;
762
763     QList<QPixmap> maps;
764     FOR(index0, dim)
765     {
766         FOR(index1, dim)
767         {
768             QPixmap map(mapW + 2*pad,mapH + 2*pad);
769             int w = map.width() - 2*pad, h = map.height() - 2*pad;
770             map.fill(Qt::white);
771             QPainter painter(&map);
772             painter.setRenderHint(QPainter::Antialiasing);
773
774             FOR(i, samples.size())
775             {
776                 float x = samples[i][index0];
777                 float y = samples[i][index1];
778                 x = (x-mins[index0])/(maxes[index0] - mins[index0]);
779                 y = (y-mins[index1])/(maxes[index1] - mins[index1]);
780                 QPointF point(y*w + pad, x*h + pad);
781                 float radius = 5;
782                 Canvas::drawSample(painter, point, radius, labels[i]);
783             }
784             painter.setBrush(Qt::NoBrush);
785             painter.setPen(Qt::black);
786             painter.setRenderHint(QPainter::Antialiasing, false);
787             painter.drawRect(pad/2,pad/2,w+pad, h+pad);
788             painter.drawText(pad/2+1, map.height()-pad/2-1, QString("e%1 x e%2").arg(index1+1).arg(index0+1));
789             maps.push_back(map);
790         }
791     }
792
793     if(bScroll)
794     {
795         pixmap = QPixmap((mapW+2*pad)*gridX, (mapH+2*pad)*gridY);
796         ui->scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
797         ui->scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
798     }
799     else
800     {
801         pixmap = QPixmap(ui->scrollArea->width(), ui->scrollArea->height());
802         ui->scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
803         ui->scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
804     }
805     pixmap.fill(Qt::white);
806     QPainter painter(&pixmap);
807     FOR(i, maps.size())
808     {
809         int xIndex = i%gridX;
810         int yIndex = i/gridX;
811         painter.drawPixmap(QPoint(xIndex*pixmap.width()/gridX, yIndex*pixmap.height()/gridY), maps[i]);
812     }
813     ui->display->setPixmap(pixmap);
814     ui->display->repaint();
815 }
816
817 void Expose::resizeEvent( QResizeEvent *event )
818 {
819     if(ui->typeCombo->currentIndex() == 0 && ui->scrollArea->horizontalScrollBarPolicy() == Qt::ScrollBarAlwaysOn)
820     {
821         GenerateScatterPlot(true);
822     }
823     else Repaint();
824     repaint();
825 }
826
827 void Expose::Repaint()
828 {
829     switch(ui->typeCombo->currentIndex())
830     {
831     case 0:
832         GenerateScatterPlot();
833         break;
834     case 1:
835         GenerateParallelCoords();
836         break;
837     case 2:
838         GenerateRadialGraph();
839         break;
840     case 3:
841         GenerateAndrewsPlots();
842         break;
843     }
844     repaint();
845 }
846
847 void Expose::Clipboard()
848 {
849     QImage image = ui->display->pixmap()->toImage();
850     QClipboard *clipboard = QApplication::clipboard();
851     clipboard->setImage(image);
852 }
853
854 void Expose::paintEvent(QPaintEvent *event)
855 {
856     QWidget::paintEvent(event);
857     if(!canvas) return;
858     if(pixmap.isNull()) Repaint();
859 }