aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--colormap.cpp113
-rw-r--r--colormap.h40
-rw-r--r--colorscale.h23
-rw-r--r--main.cpp33
-rw-r--r--main_view.qml9
-rw-r--r--pm.pro2
-rw-r--r--scatterplot.cpp6
-rw-r--r--scatterplot.h2
-rw-r--r--voronoisplat.cpp101
-rw-r--r--voronoisplat.h4
10 files changed, 265 insertions, 68 deletions
diff --git a/colormap.cpp b/colormap.cpp
new file mode 100644
index 0000000..545151f
--- /dev/null
+++ b/colormap.cpp
@@ -0,0 +1,113 @@
+#include "colormap.h"
+
+#include <QOpenGLFunctions>
+#include <QSGSimpleTextureNode>
+
+class ColormapTexture
+ : public QSGDynamicTexture
+{
+public:
+ ColormapTexture(const std::vector<float> *cmap);
+ ~ColormapTexture();
+
+ bool hasAlphaChannel() const { return false; }
+ bool hasMipmaps() const { return false; }
+ void bind();
+ bool updateTexture();
+
+ int textureId() const { return m_texture; }
+ QSize textureSize() const { return m_size; }
+
+private:
+ QOpenGLFunctions gl;
+
+ QSize m_size;
+ GLuint m_texture;
+ const std::vector<float> *m_cmap;
+};
+
+ColormapTexture::ColormapTexture(const std::vector<float> *cmap)
+ : gl(QOpenGLContext::currentContext())
+ , m_size(Colormap::SAMPLES, 1)
+ , m_cmap(cmap)
+{
+ // Setup OpenGL texture
+ gl.glGenTextures(1, &m_texture);
+ gl.glBindTexture(GL_TEXTURE_2D, m_texture);
+ gl.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, m_size.width(), m_size.height(),
+ 0, GL_RGB, GL_FLOAT, 0);
+ gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+}
+
+ColormapTexture::~ColormapTexture()
+{
+ gl.glDeleteTextures(1, &m_texture);
+}
+
+void ColormapTexture::bind()
+{
+ gl.glBindTexture(GL_TEXTURE_2D, m_texture);
+}
+
+bool ColormapTexture::updateTexture()
+{
+ gl.glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_size.width(), m_size.height(),
+ GL_RGB, GL_FLOAT, m_cmap->data());
+ return true;
+}
+
+Colormap::Colormap(QQuickItem *parent)
+ : QQuickItem(parent)
+ , m_texture(0)
+ , m_shouldUpdateTexture(false)
+ , m_cmap(3*SAMPLES)
+{
+ setFlag(QQuickItem::ItemHasContents);
+}
+
+Colormap::~Colormap()
+{
+ if (m_texture) {
+ delete m_texture;
+ }
+}
+
+
+void Colormap::setColorScale(const ColorScale &scale)
+{
+ scale.sample(SAMPLES, m_cmap.begin());
+ emit colorScaleChanged(scale);
+
+ m_shouldUpdateTexture = true;
+ update();
+}
+
+QSGNode *Colormap::newSceneGraph()
+{
+ QSGSimpleTextureNode *node = new QSGSimpleTextureNode;
+ m_texture = new ColormapTexture(&m_cmap);
+
+ node->setTexture(m_texture);
+ node->setOwnsTexture(false);
+
+ const QSize &texSize = m_texture->textureSize();
+ node->setSourceRect(0, 0, texSize.width(), texSize.height());
+
+ return node;
+}
+
+QSGNode *Colormap::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
+{
+ QSGNode *root = oldNode ? oldNode : newSceneGraph();
+
+ QSGSimpleTextureNode *node = static_cast<QSGSimpleTextureNode *>(root);
+ node->setRect(x(), y(), width(), height());
+
+ if (m_shouldUpdateTexture) {
+ m_texture->updateTexture();
+ m_shouldUpdateTexture = false;
+ }
+
+ return root;
+}
diff --git a/colormap.h b/colormap.h
new file mode 100644
index 0000000..806624a
--- /dev/null
+++ b/colormap.h
@@ -0,0 +1,40 @@
+#ifndef COLORMAP_H
+#define COLORMAP_H
+
+#include <vector>
+
+#include <QtQuick>
+#include <QSGDynamicTexture>
+
+#include <armadillo>
+
+#include "colorscale.h"
+
+class Colormap
+ : public QQuickItem
+{
+ Q_OBJECT
+public:
+ static const int SAMPLES = 128;
+
+ Colormap(QQuickItem *parent = 0);
+ ~Colormap();
+
+signals:
+ void colorScaleChanged(const ColorScale &scale) const;
+
+public slots:
+ void setColorScale(const ColorScale &scale);
+
+protected:
+ QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *);
+
+private:
+ QSGNode *newSceneGraph();
+
+ QSGDynamicTexture *m_texture;
+ bool m_shouldUpdateTexture;
+ std::vector<float> m_cmap;
+};
+
+#endif // COLORMAP_H
diff --git a/colorscale.h b/colorscale.h
index af74b5c..90671eb 100644
--- a/colorscale.h
+++ b/colorscale.h
@@ -20,9 +20,32 @@ public:
float min() const { return m_min; }
float max() const { return m_max; }
+ template<typename OutputIterator>
+ void sample(int samples, OutputIterator it) const;
+
protected:
float m_min, m_max;
QList<QColor> m_colors;
};
+template<typename OutputIterator>
+void ColorScale::sample(int samples, OutputIterator it) const
+{
+ if (samples < 1) {
+ return;
+ }
+
+ float t = min();
+ float step = (max() - min()) / samples;
+ qreal r, g, b;
+ for (unsigned i = 0; i < 3*samples; i += 3) {
+ color(t).getRgbF(&r, &g, &b);
+ *it = r; it++;
+ *it = g; it++;
+ *it = b; it++;
+
+ t += step;
+ }
+}
+
#endif // COLORSCALE_H
diff --git a/main.cpp b/main.cpp
index 4d43c4c..b79f6ff 100644
--- a/main.cpp
+++ b/main.cpp
@@ -15,6 +15,7 @@
#include "voronoisplat.h"
#include "historygraph.h"
#include "barchart.h"
+#include "colormap.h"
#include "interactionhandler.h"
#include "selectionhandler.h"
#include "skelft.h"
@@ -97,6 +98,7 @@ int main(int argc, char **argv)
qmlRegisterType<HistoryGraph>("PM", 1, 0, "HistoryGraph");
qmlRegisterType<BarChart>("PM", 1, 0, "BarChart");
qmlRegisterType<VoronoiSplat>("PM", 1, 0, "VoronoiSplat");
+ qmlRegisterType<Colormap>("PM", 1, 0, "Colormap");
qmlRegisterType<InteractionHandler>("PM", 1, 0, "InteractionHandler");
qmlRegisterSingletonType<Main>("PM", 1, 0, "Main", mainProvider);
@@ -113,21 +115,22 @@ int main(int argc, char **argv)
QQmlApplicationEngine engine(QUrl("qrc:///main_view.qml"));
- //ColorScale colorScale{
- // QColor("#1f77b4"),
- // QColor("#ff7f0e"),
- // QColor("#2ca02c"),
- // QColor("#d62728"),
- // QColor("#9467bd"),
- // QColor("#8c564b"),
- // QColor("#e377c2"),
- // QColor("#17becf"),
- // QColor("#7f7f7f"),
- //};
+ ColorScale colorScale{
+ QColor("#1f77b4"),
+ QColor("#ff7f0e"),
+ QColor("#2ca02c"),
+ QColor("#d62728"),
+ QColor("#9467bd"),
+ QColor("#8c564b"),
+ QColor("#e377c2"),
+ QColor("#17becf"),
+ QColor("#7f7f7f"),
+ };
+ colorScale.setExtents(labels.min(), labels.max());
+
+ //ContinuousColorScale colorScale = ContinuousColorScale::builtin(ContinuousColorScale::RED_GRAY_BLUE);
//colorScale.setExtents(labels.min(), labels.max());
- ContinuousColorScale colorScale = ContinuousColorScale::builtin(ContinuousColorScale::RED_GRAY_BLUE);
- colorScale.setExtents(labels.min(), labels.max());
Scatterplot *cpPlot = engine.rootObjects()[0]->findChild<Scatterplot *>("cpPlot");
cpPlot->setAcceptedMouseButtons(Qt::LeftButton | Qt::MiddleButton | Qt::RightButton);
// cpPlot->setColorData(arma::zeros<arma::vec>(cpSize));
@@ -135,6 +138,7 @@ int main(int argc, char **argv)
Scatterplot *plot = engine.rootObjects()[0]->findChild<Scatterplot *>("plot");
VoronoiSplat *splat = engine.rootObjects()[0]->findChild<VoronoiSplat *>("splat");
skelft2DInitialization(splat->width());
+ Colormap *colormap = engine.rootObjects()[0]->findChild<Colormap *>("colormap");
// Keep track of the current cp (in order to save them later, if requested)
QObject::connect(cpPlot, SIGNAL(xyChanged(const arma::mat &)),
@@ -176,10 +180,11 @@ int main(int argc, char **argv)
barChart->setValues(arma::randn<arma::vec>(100));
//history->addHistoryItem(Ys);
+ colormap->setColorScale(colorScale);
plot->setColorScale(&colorScale);
plot->setColorData(labels, false);
+ //splat->setColormap(colorScale);
splat->setValues(labels);
- splat->update();
cpPlot->setAutoScale(false);
cpPlot->setColorData(labels(cpIndices), false);
diff --git a/main_view.qml b/main_view.qml
index dc3fe45..68d6fea 100644
--- a/main_view.qml
+++ b/main_view.qml
@@ -125,6 +125,15 @@ ApplicationWindow {
y: parent.y
anchors.fill: parent
}
+
+ Colormap {
+ id: colormap
+ objectName: "colormap"
+ x: parent.x + 5
+ y: parent.y + 5
+ width: 128
+ height: 10
+ }
}
Rectangle {
diff --git a/pm.pro b/pm.pro
index 54476c8..53ac0a5 100644
--- a/pm.pro
+++ b/pm.pro
@@ -11,6 +11,7 @@ HEADERS += main.h \
scale.h \
scatterplot.h \
voronoisplat.h \
+ colormap.h \
historygraph.h \
barchart.h \
interactionhandler.h \
@@ -28,6 +29,7 @@ SOURCES += main.cpp \
geometry.cpp \
scatterplot.cpp \
voronoisplat.cpp \
+ colormap.cpp \
historygraph.cpp \
barchart.cpp \
interactionhandler.cpp \
diff --git a/scatterplot.cpp b/scatterplot.cpp
index 04a50cd..5b7e82d 100644
--- a/scatterplot.cpp
+++ b/scatterplot.cpp
@@ -12,7 +12,6 @@ static const QColor SELECTION_COLOR(128, 128, 128, 96);
static const float GLYPH_SIZE = 8.0f;
static const float GLYPH_OUTLINE_WIDTH = 2.0f;
-static const float PADDING = 10.0f;
Scatterplot::Scatterplot(QQuickItem *parent)
: QQuickItem(parent)
@@ -239,7 +238,6 @@ QSGNode *Scatterplot::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
updateGlyphs(node);
node = node->nextSibling();
- // Change update hints to false; the splat and glyphs were just updated
if (m_shouldUpdateGeometry) {
m_shouldUpdateGeometry = false;
}
@@ -265,6 +263,10 @@ void Scatterplot::updateGlyphs(QSGNode *glyphsNode)
{
qreal x, y, tx, ty, moveTranslationF;
+ if (!m_shouldUpdateGeometry && !m_shouldUpdateMaterials) {
+ return;
+ }
+
if (m_currentInteractionState == INTERACTION_MOVING) {
tx = m_dragCurrentPos.x() - m_dragOriginPos.x();
ty = m_dragCurrentPos.y() - m_dragOriginPos.y();
diff --git a/scatterplot.h b/scatterplot.h
index 656ed09..f577819 100644
--- a/scatterplot.h
+++ b/scatterplot.h
@@ -25,6 +25,8 @@ public:
void setAutoScale(bool autoScale);
Q_INVOKABLE bool saveToFile(const QUrl &url);
+ static const int PADDING = 10;
+
signals:
void xyChanged(const arma::mat &XY) const;
void xyInteractivelyChanged(const arma::mat &XY) const;
diff --git a/voronoisplat.cpp b/voronoisplat.cpp
index a33844d..3b11619 100644
--- a/voronoisplat.cpp
+++ b/voronoisplat.cpp
@@ -7,12 +7,12 @@
#include <QOpenGLShaderProgram>
#include <QOpenGLVertexArrayObject>
+#include "colormap.h"
#include "scale.h"
+#include "scatterplot.h"
#include "skelft.h"
static const float RAD_BLUR = 5.0f;
-static const int COLORMAP_SAMPLES = 128;
-static const float PADDING = 10.0f; // in screen pixels
static int nextPow2(int n)
{
@@ -64,7 +64,7 @@ private:
VoronoiSplat::VoronoiSplat(QQuickItem *parent)
: QQuickFramebufferObject(parent)
- , m_cmap(3*COLORMAP_SAMPLES)
+ , m_cmap(3*Colormap::SAMPLES)
{
setTextureFollowsItemSize(false);
}
@@ -84,19 +84,20 @@ void VoronoiSplat::setSites(const arma::mat &points)
// Coords are packed into 'm_sites' as [ x1, y1, x2, y2, ... ]
m_sites.resize(2*points.n_rows);
- LinearScale<float> sx(minX, maxX, PADDING, width() - PADDING);
+ LinearScale<float> sx(minX, maxX, Scatterplot::PADDING, width() - Scatterplot::PADDING);
const double *col = points.colptr(0);
for (unsigned i = 0; i < points.n_rows; i++) {
m_sites[2*i] = sx(col[i]);
}
col = points.colptr(1);
- LinearScale<float> sy(minY, maxY, height() - PADDING, PADDING);
+ LinearScale<float> sy(minY, maxY, height() - Scatterplot::PADDING, Scatterplot::PADDING);
for (unsigned i = 0; i < points.n_rows; i++) {
m_sites[2*i + 1] = sy(col[i]);
}
setSitesChanged(true);
+ update();
}
void VoronoiSplat::setValues(const arma::vec &values)
@@ -109,29 +110,19 @@ void VoronoiSplat::setValues(const arma::vec &values)
m_values.resize(values.n_elem);
LinearScale<float> scale(values.min(), values.max(), 0, 1.0f);
std::transform(values.begin(), values.end(), m_values.begin(), scale);
+ emit valuesChanged(values);
setValuesChanged(true);
+ update();
}
-void VoronoiSplat::setColormap(const ColorScale *scale)
+void VoronoiSplat::setColormap(const ColorScale &scale)
{
- if (!scale) {
- return;
- }
-
- float t = scale->min();
- float step = (scale->max() - scale->min()) / COLORMAP_SAMPLES;
- qreal r, g, b;
- for (unsigned i = 0; i < m_cmap.size(); i += 3) {
- scale->color(t).getRgbF(&r, &g, &b);
- m_cmap[i + 0] = r;
- m_cmap[i + 1] = g;
- m_cmap[i + 2] = b;
-
- t += step;
- }
+ scale.sample(Colormap::SAMPLES, m_cmap.begin());
+ emit colormapChanged(scale);
setColormapChanged(true);
+ update();
}
QQuickFramebufferObject::Renderer *VoronoiSplat::createRenderer() const
@@ -274,29 +265,12 @@ void VoronoiSplatRenderer::setupVAOs()
void VoronoiSplatRenderer::setupTextures()
{
gl.glGenTextures(2, m_textures);
- gl.glGenTextures(1, &m_colormapTex);
-}
-
-void VoronoiSplatRenderer::resizeTextures()
-{
- // textures[0] stores the DT values for each pixel
- gl.glBindTexture(GL_TEXTURE_2D, m_textures[0]);
- gl.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, m_size.width(),
- m_size.height(), 0, GL_RG, GL_FLOAT, 0);
- gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-
- // textures[1] is the result of the first pass
- gl.glBindTexture(GL_TEXTURE_2D, m_textures[1]);
- gl.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, m_size.width(),
- m_size.height(), 0, GL_RGBA, GL_FLOAT, 0);
- gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// Used for colormap lookup in the frag shader
// (2D texture for compatibility; used to be a 1D texture)
+ gl.glGenTextures(1, &m_colormapTex);
gl.glBindTexture(GL_TEXTURE_2D, m_colormapTex);
- gl.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, COLORMAP_SAMPLES, 1, 0, GL_RGB,
+ gl.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, Colormap::SAMPLES, 1, 0, GL_RGB,
GL_FLOAT, 0);
gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
@@ -314,6 +288,23 @@ VoronoiSplatRenderer::~VoronoiSplatRenderer()
delete m_program2;
}
+void VoronoiSplatRenderer::resizeTextures()
+{
+ // textures[0] stores the DT values for each pixel
+ gl.glBindTexture(GL_TEXTURE_2D, m_textures[0]);
+ gl.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, m_size.width(),
+ m_size.height(), 0, GL_RED, GL_FLOAT, 0);
+ gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+ // textures[1] is the result of the first pass
+ gl.glBindTexture(GL_TEXTURE_2D, m_textures[1]);
+ gl.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, m_size.width(),
+ m_size.height(), 0, GL_RGBA, GL_FLOAT, 0);
+ gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+}
+
QOpenGLFramebufferObject *VoronoiSplatRenderer::createFramebufferObject(const QSize &size)
{
int baseSize = nextPow2(std::min(size.width(), size.height()));
@@ -339,6 +330,7 @@ void VoronoiSplatRenderer::render()
int originalFBO;
gl.glGetIntegerv(GL_FRAMEBUFFER_BINDING, &originalFBO);
+
gl.glBindFramebuffer(GL_FRAMEBUFFER, m_FBO);
// First pass
@@ -370,6 +362,9 @@ void VoronoiSplatRenderer::render()
m_program1->release();
+ // For some reason this call makes the splat circle of the correct size
+ //m_window->resetOpenGLState();
+
// Second pass
m_program2->bind();
m_program2->setUniformValue("rad_max", 20.0f);
@@ -408,6 +403,12 @@ void VoronoiSplatRenderer::synchronize(QQuickFramebufferObject *item)
m_sitesChanged = splat->sitesChanged();
m_valuesChanged = splat->valuesChanged();
m_colormapChanged = splat->colormapChanged();
+
+ // Reset these so that by the next synchronize() we have the correct values
+ splat->setSitesChanged(false);
+ splat->setValuesChanged(false);
+ splat->setColormapChanged(false);
+
m_sites = &(splat->sites());
m_values = &(splat->values());
m_cmap = &(splat->colormap());
@@ -437,8 +438,11 @@ void VoronoiSplatRenderer::updateValues()
void VoronoiSplatRenderer::updateColormap()
{
+ gl.glActiveTexture(GL_TEXTURE0);
gl.glBindTexture(GL_TEXTURE_2D, m_colormapTex);
- gl.glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, COLORMAP_SAMPLES, 1, GL_RGB,
+ //gl.glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, Colormap::SAMPLES, 1, GL_RGB,
+ // GL_FLOAT, m_cmap->data());
+ gl.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, Colormap::SAMPLES, 1, 0, GL_RGB,
GL_FLOAT, m_cmap->data());
m_colormapChanged = false;
@@ -452,21 +456,18 @@ void VoronoiSplatRenderer::computeDT()
std::vector<float> buf(w*h);
const std::vector<float> &sites = *m_sites;
for (unsigned i = 0; i < sites.size(); i += 2) {
- buf[int(sites[i + 1])*h + int(sites[i])] = (float) i/2 + 1;
+ buf[int(sites[i + 1])*h + int(sites[i])] = i/2.0f + 1.0f;
}
skelft2DFT(0, buf.data(), 0, 0, w, h, w);
// Compute DT of the sites (from the resident FT)
skelft2DDT(buf.data(), 0, 0, w, h);
- std::vector<GLfloat> dtTexData(2*w*h, 0.0f);
- for (unsigned i = 0; i < buf.size(); i++) {
- dtTexData[2*i] = buf[i];
- }
-
// Upload result to lookup texture
+ gl.glActiveTexture(GL_TEXTURE0);
gl.glBindTexture(GL_TEXTURE_2D, m_textures[0]);
- gl.glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RG, GL_FLOAT,
- dtTexData.data());
+ //gl.glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RED, GL_FLOAT,
+ // buf.data());
+ gl.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, w, h, 0, GL_RED, GL_FLOAT,
+ buf.data());
}
-
diff --git a/voronoisplat.h b/voronoisplat.h
index fb93f09..6061ef3 100644
--- a/voronoisplat.h
+++ b/voronoisplat.h
@@ -31,7 +31,7 @@ public:
signals:
void sitesChanged(const arma::mat &sites);
void valuesChanged(const arma::vec &values);
- void colormapChanged(const ColorScale *scale);
+ void colormapChanged(const ColorScale &scale);
public slots:
// 'points' should be a 2D points matrix (each point in a row)
@@ -41,7 +41,7 @@ public slots:
void setValues(const arma::vec &values);
// Set colormap data based on the given color scale
- void setColormap(const ColorScale *scale);
+ void setColormap(const ColorScale &scale);
private:
std::vector<float> m_sites, m_values, m_cmap;