1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
|
#include "scatterplot.h"
#include <iostream>
#include <cmath>
#include <QSGNode>
#include <QSGGeometry>
#include <QSGGeometryNode>
#include <QSGMaterial>
#include <QSGFlatColorMaterial>
#include <QSGSimpleRectNode>
const int GLYPH_SIZE = 5;
const float PADDING = 10;
const float PI = 3.1415f;
Scatterplot::Scatterplot(QQuickItem *parent)
: QQuickItem(parent)
, m_colorScale{QColor("red"), QColor("green"), QColor("blue")}
{
setFlag(QQuickItem::ItemHasContents);
}
Scatterplot::~Scatterplot()
{
}
void Scatterplot::setData(const arma::mat &data)
{
if (data.n_cols != 3)
return;
m_data = data;
m_colorScale.setExtents(m_data.col(2).min(), m_data.col(2).max());
update();
}
int calculateCircleVertexCount(qreal radius)
{
// 10 * sqrt(r) \approx 2*pi / acos(1 - 1 / (4*r))
return (int) (10.0 * sqrt(radius));
}
void updateCircleGeometry(QSGGeometry *geometry, float size, float cx, float cy)
{
int vertexCount = geometry->vertexCount();
float theta = 2 * PI / float(vertexCount);
float c = cosf(theta);
float s = sinf(theta);
float x = size / 2;
float y = 0;
QSGGeometry::Point2D *vertexData = geometry->vertexDataAsPoint2D();
for (int i = 0; i < vertexCount; i++) {
vertexData[i].set(x + cx, y + cy);
float t = x;
x = c*x - s*y;
y = s*t + c*y;
}
}
void updateSquareGeometry(QSGGeometry *geometry, float size, float cx, float cy)
{
float r = size / 2;
QSGGeometry::Point2D *vertexData = geometry->vertexDataAsPoint2D();
vertexData[0].set(cx - r, cy - r);
vertexData[1].set(cx + r, cy - r);
vertexData[2].set(cx + r, cy + r);
vertexData[3].set(cx - r, cy + r);
}
QSGNode *Scatterplot::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
{
if (m_data.n_rows < 1)
return 0;
QSGNode *node = 0;
int vertexCount = calculateCircleVertexCount(GLYPH_SIZE / 2);
qreal xmin = m_data.col(0).min(),
xmax = m_data.col(0).max(),
ymin = m_data.col(1).min(),
ymax = m_data.col(1).max(),
x, y;
if (!oldNode) {
node = new QSGNode;
for (arma::uword i = 0; i < m_data.n_rows; i++) {
QSGGeometryNode *childNode = new QSGGeometryNode;
QSGGeometry *geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), vertexCount);
geometry->setDrawingMode(GL_POLYGON);
childNode->setGeometry(geometry);
childNode->setFlag(QSGNode::OwnsGeometry);
QSGFlatColorMaterial *material = new QSGFlatColorMaterial;
material->setColor(m_colorScale.color(m_data(i, 2)));
childNode->setMaterial(material);
childNode->setFlag(QSGNode::OwnsMaterial);
node->appendChildNode(childNode);
}
} else {
node = oldNode;
}
QSGNode *childNode = node->firstChild();
for (arma::uword i = 0; i < m_data.n_rows; i++) {
arma::rowvec row = m_data.row(i);
x = PADDING + (row[0] - xmin) / (xmax - xmin) * (width() - 2*PADDING);
y = PADDING + (row[1] - ymin) / (ymax - ymin) * (height() - 2*PADDING);
QSGGeometry *geometry = static_cast<QSGGeometryNode *>(childNode)->geometry();
updateCircleGeometry(geometry, GLYPH_SIZE, x, y);
childNode->markDirty(QSGNode::DirtyGeometry);
childNode = childNode->nextSibling();
}
return node;
}
|