aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSamuel Fadel <samuelfadel@gmail.com>2016-01-15 22:35:13 +0100
committerSamuel Fadel <samuelfadel@gmail.com>2016-01-15 22:35:13 +0100
commit7ad4b526899f8bc764192e88e65306c788a9ed32 (patch)
treeb2e7dae9ceb06b12709ab3b76fc749304c7a2a92
parente3a4e5827ada7836468d785b12a57e0022b653e6 (diff)
VoronoiSplat & Scatterplot: splatting is now a separate component.
The change was due to future functionality requirements, this separation provides grater flexibility. As a nice side effect, the cropping bug when first rendering the splat is now gone.
-rw-r--r--main.cpp37
-rw-r--r--main_view.qml8
-rw-r--r--scatterplot.cpp71
-rw-r--r--scatterplot.h16
-rw-r--r--voronoisplat.cpp278
-rw-r--r--voronoisplat.h61
6 files changed, 227 insertions, 244 deletions
diff --git a/main.cpp b/main.cpp
index 22b8d67..4d43c4c 100644
--- a/main.cpp
+++ b/main.cpp
@@ -96,6 +96,7 @@ int main(int argc, char **argv)
qmlRegisterType<Scatterplot>("PM", 1, 0, "Scatterplot");
qmlRegisterType<HistoryGraph>("PM", 1, 0, "HistoryGraph");
qmlRegisterType<BarChart>("PM", 1, 0, "BarChart");
+ qmlRegisterType<VoronoiSplat>("PM", 1, 0, "VoronoiSplat");
qmlRegisterType<InteractionHandler>("PM", 1, 0, "InteractionHandler");
qmlRegisterSingletonType<Main>("PM", 1, 0, "Main", mainProvider);
@@ -112,28 +113,28 @@ 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(-1, 1);
Scatterplot *cpPlot = engine.rootObjects()[0]->findChild<Scatterplot *>("cpPlot");
- cpPlot->setDisplaySplat(false);
cpPlot->setAcceptedMouseButtons(Qt::LeftButton | Qt::MiddleButton | Qt::RightButton);
// cpPlot->setColorData(arma::zeros<arma::vec>(cpSize));
cpPlot->setColorScale(&colorScale);
Scatterplot *plot = engine.rootObjects()[0]->findChild<Scatterplot *>("plot");
- skelft2DInitialization(plot->width());
+ VoronoiSplat *splat = engine.rootObjects()[0]->findChild<VoronoiSplat *>("splat");
+ skelft2DInitialization(splat->width());
// Keep track of the current cp (in order to save them later, if requested)
QObject::connect(cpPlot, SIGNAL(xyChanged(const arma::mat &)),
@@ -150,6 +151,8 @@ int main(int argc, char **argv)
&interactionHandler, SLOT(setCP(const arma::mat &)));
QObject::connect(&interactionHandler, SIGNAL(cpChanged(const arma::mat &)),
plot, SLOT(setXY(const arma::mat &)));
+ QObject::connect(&interactionHandler, SIGNAL(cpChanged(const arma::mat &)),
+ splat, SLOT(setSites(const arma::mat &)));
m->setTechnique(InteractionHandler::TECHNIQUE_LAMP);
// Linking between selections in cp plot and full dataset plot
@@ -175,6 +178,8 @@ int main(int argc, char **argv)
//history->addHistoryItem(Ys);
plot->setColorScale(&colorScale);
plot->setColorData(labels, false);
+ splat->setValues(labels);
+ splat->update();
cpPlot->setAutoScale(false);
cpPlot->setColorData(labels(cpIndices), false);
diff --git a/main_view.qml b/main_view.qml
index a0290e1..dc3fe45 100644
--- a/main_view.qml
+++ b/main_view.qml
@@ -102,6 +102,14 @@ ApplicationWindow {
border.width: 1
border.color: "#cccccc"
+ VoronoiSplat {
+ id: splat
+ objectName: "splat"
+ x: parent.x
+ y: parent.y
+ anchors.fill: parent
+ }
+
Scatterplot {
id: plot
objectName: "plot"
diff --git a/scatterplot.cpp b/scatterplot.cpp
index de96e6f..04a50cd 100644
--- a/scatterplot.cpp
+++ b/scatterplot.cpp
@@ -1,6 +1,5 @@
#include "scatterplot.h"
-#include "voronoisplat.h"
#include "geometry.h"
#include <cmath>
@@ -23,7 +22,6 @@ Scatterplot::Scatterplot(QQuickItem *parent)
, m_currentInteractionState(INTERACTION_NONE)
, m_shouldUpdateGeometry(false)
, m_shouldUpdateMaterials(false)
- , m_displaySplat(true)
{
setClip(true);
setFlag(QQuickItem::ItemHasContents);
@@ -164,26 +162,6 @@ void Scatterplot::autoScale()
emit scaleChanged(m_sx, m_sy);
}
-QSGNode *Scatterplot::newSplatNode()
-{
- if (m_xy.n_rows < 1) {
- return 0;
- }
-
- QSGSimpleTextureNode *node = new QSGSimpleTextureNode;
- VoronoiSplatTexture *tex = new VoronoiSplatTexture(QSize(width(), height()));
-
- tex->setSites(m_xy);
- tex->setValues(m_colorData);
- tex->setColormap(m_colorScale);
-
- node->setTexture(tex);
- node->setOwnsTexture(true);
- node->setSourceRect(0, 0, width(), height());
-
- return node;
-}
-
QSGNode *Scatterplot::newGlyphTree()
{
// NOTE:
@@ -235,10 +213,6 @@ QSGNode *Scatterplot::newSceneGraph()
// The hierarchy in the scene graph is as follows:
// root [[splatNode] [glyphsRoot [glyph [...]]] [selectionNode]]
QSGNode *root = new QSGNode;
- QSGNode *splatNode = newSplatNode();
- if (splatNode) {
- root->appendChildNode(splatNode);
- }
QSGNode *glyphTreeRoot = newGlyphTree();
if (glyphTreeRoot) {
root->appendChildNode(glyphTreeRoot);
@@ -262,9 +236,6 @@ QSGNode *Scatterplot::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
// This keeps track of where we are in the scene when updating
QSGNode *node = root->firstChild();
- updateSplat(node);
- node = node->nextSibling();
-
updateGlyphs(node);
node = node->nextSibling();
@@ -290,36 +261,6 @@ QSGNode *Scatterplot::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
return root;
}
-void Scatterplot::updateSplat(QSGNode *node)
-{
- QSGSimpleTextureNode *texNode = static_cast<QSGSimpleTextureNode *>(node);
-
- if (!m_displaySplat) {
- // Hide the splat and return, ignoring update requests
- texNode->setRect(0, 0, 0, 0);
- return;
- }
-
- texNode->setRect(x(), y(), width(), height());
-
- VoronoiSplatTexture *tex =
- static_cast<VoronoiSplatTexture *>(texNode->texture());
- if (m_shouldUpdateGeometry) {
- tex->setSites(m_xy);
- }
-
- if (m_shouldUpdateMaterials) {
- tex->setValues(m_colorData);
- tex->setColormap(m_colorScale);
- }
-
- bool updated = tex->updateTexture();
- if (updated) {
- texNode->markDirty(QSGNode::DirtyMaterial);
- window()->resetOpenGLState();
- }
-}
-
void Scatterplot::updateGlyphs(QSGNode *glyphsNode)
{
qreal x, y, tx, ty, moveTranslationF;
@@ -487,18 +428,6 @@ void Scatterplot::setSelection(const QSet<int> &selection)
emit selectionChanged(selection);
}
-void Scatterplot::setDisplaySplat(bool displaySplat)
-{
- if (m_displaySplat != displaySplat) {
- m_displaySplat = displaySplat;
- m_shouldUpdateGeometry = true;
- m_shouldUpdateMaterials = true;
- update();
-
- emit displaySplatChanged(displaySplat);
- }
-}
-
void Scatterplot::applyManipulation()
{
m_sx.inverse();
diff --git a/scatterplot.h b/scatterplot.h
index 6510c54..656ed09 100644
--- a/scatterplot.h
+++ b/scatterplot.h
@@ -1,16 +1,16 @@
#ifndef SCATTERPLOT_H
#define SCATTERPLOT_H
-#include <memory>
-#include <armadillo>
#include <QtQuick>
#include <QSet>
-#include <QEasingCurve>
+
+#include <armadillo>
#include "colorscale.h"
#include "scale.h"
-class Scatterplot : public QQuickItem
+class Scatterplot
+ : public QQuickItem
{
Q_OBJECT
public:
@@ -31,7 +31,6 @@ signals:
void colorDataChanged(const arma::vec &colorData) const;
void opacityDataChanged(const arma::vec &opacityData) const;
void selectionChanged(const QSet<int> &selection) const;
- void displaySplatChanged(bool displaySplat) const;
void scaleChanged(const LinearScale<float> &sx, const LinearScale<float> &sy) const;
public slots:
@@ -39,7 +38,6 @@ public slots:
void setColorData(const arma::vec &colorData);
void setOpacityData(const arma::vec &opacityData);
void setSelection(const QSet<int> &selection);
- void setDisplaySplat(bool displaySplat);
void setScale(const LinearScale<float> &sx, const LinearScale<float> &sy);
protected:
@@ -50,15 +48,14 @@ protected:
private:
QSGNode *newSceneGraph();
- QSGNode *newSplatNode();
QSGNode *newGlyphTree();
- bool updateSelection(bool mergeSelection);
void applyManipulation();
- void updateSplat(QSGNode *node);
void updateGlyphs(QSGNode *node);
+ bool updateSelection(bool mergeSelection);
+
arma::mat m_xy;
void autoScale();
@@ -79,7 +76,6 @@ private:
bool m_shouldUpdateGeometry, m_shouldUpdateMaterials;
- bool m_displaySplat;
ColorScale *m_colorScale;
arma::vec m_colorData;
diff --git a/voronoisplat.cpp b/voronoisplat.cpp
index 468bcf2..a33844d 100644
--- a/voronoisplat.cpp
+++ b/voronoisplat.cpp
@@ -3,7 +3,9 @@
#include <algorithm>
#include <QQuickWindow>
-#include <QOpenGLFramebufferObject>
+#include <QOpenGLFunctions>
+#include <QOpenGLShaderProgram>
+#include <QOpenGLVertexArrayObject>
#include "scale.h"
#include "skelft.h"
@@ -22,21 +24,132 @@ static int nextPow2(int n)
return n + 1;
}
-VoronoiSplatTexture::VoronoiSplatTexture(const QSize &size)
- : gl(QOpenGLContext::currentContext())
+class VoronoiSplatRenderer
+ : public QQuickFramebufferObject::Renderer
+{
+public:
+ // 'size' must be square (and power of 2)
+ VoronoiSplatRenderer();
+ virtual ~VoronoiSplatRenderer();
+
+protected:
+ QOpenGLFramebufferObject *createFramebufferObject(const QSize &size);
+ void render();
+ void synchronize(QQuickFramebufferObject *item);
+
+private:
+ void setupShaders();
+ void setupVAOs();
+ void setupTextures();
+ void resizeTextures();
+
+ void updateSites();
+ void updateValues();
+ void updateColormap();
+ void computeDT();
+
+ QSize m_size;
+ const std::vector<float> *m_sites, *m_values, *m_cmap;
+
+ QQuickWindow *m_window; // used to reset OpenGL state (as per docs)
+ QOpenGLFunctions gl;
+ QOpenGLShaderProgram *m_program1, *m_program2;
+ GLuint m_FBO;
+ GLuint m_VBOs[3];
+ GLuint m_textures[2], m_colormapTex;
+ QOpenGLVertexArrayObject m_sitesVAO, m_2ndPassVAO;
+ bool m_sitesChanged, m_valuesChanged, m_colormapChanged;
+};
+
+
+VoronoiSplat::VoronoiSplat(QQuickItem *parent)
+ : QQuickFramebufferObject(parent)
, m_cmap(3*COLORMAP_SAMPLES)
{
- int baseSize = nextPow2(std::min(size.width(), size.height()));
- m_size.setWidth(baseSize);
- m_size.setHeight(baseSize);
+ setTextureFollowsItemSize(false);
+}
+
+void VoronoiSplat::setSites(const arma::mat &points)
+{
+ if (points.n_rows < 1 || points.n_cols != 2
+ || (m_values.size() != 0 && points.n_rows != m_values.size())) {
+ return;
+ }
+
+ // Copy 'points' to internal data structure(s)
+ double minX = points.col(0).min();
+ double maxX = points.col(0).max();
+ double minY = points.col(1).min();
+ double maxY = points.col(1).max();
+
+ // 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);
+ 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);
+ for (unsigned i = 0; i < points.n_rows; i++) {
+ m_sites[2*i + 1] = sy(col[i]);
+ }
+
+ setSitesChanged(true);
+}
+
+void VoronoiSplat::setValues(const arma::vec &values)
+{
+ if (values.n_elem == 0
+ || (m_sites.size() != 0 && values.n_elem != m_sites.size() / 2)) {
+ return;
+ }
+
+ 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);
+
+ setValuesChanged(true);
+}
+
+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;
+ }
+
+ setColormapChanged(true);
+}
+QQuickFramebufferObject::Renderer *VoronoiSplat::createRenderer() const
+{
+ return new VoronoiSplatRenderer;
+}
+
+VoronoiSplatRenderer::VoronoiSplatRenderer()
+ : gl(QOpenGLContext::currentContext())
+{
gl.glGenFramebuffers(1, &m_FBO);
+
setupShaders();
setupVAOs();
setupTextures();
}
-void VoronoiSplatTexture::setupShaders()
+void VoronoiSplatRenderer::setupShaders()
{
m_program1 = new QOpenGLShaderProgram;
m_program1->addShaderFromSourceCode(QOpenGLShader::Vertex,
@@ -110,7 +223,7 @@ uniform float rad_max;
layout (location = 0) out vec4 fragColor;
vec3 getRGB(float value) {
- return texture(colormap, vec2(mix(0.005, 0.995, value), 0)).rgb;
+ return texture(colormap, vec2(value, 0)).rgb;
}
void main() {
@@ -127,7 +240,7 @@ void main() {
m_program2->link();
}
-void VoronoiSplatTexture::setupVAOs()
+void VoronoiSplatRenderer::setupVAOs()
{
gl.glGenBuffers(3, m_VBOs);
@@ -158,10 +271,14 @@ void VoronoiSplatTexture::setupVAOs()
m_2ndPassVAO.release();
}
-void VoronoiSplatTexture::setupTextures()
+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(),
@@ -177,29 +294,19 @@ void VoronoiSplatTexture::setupTextures()
gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// Used for colormap lookup in the frag shader
- // (using a 2D texture for compatibility; used to be a 1D texture)
- gl.glGenTextures(1, &m_colormapTex);
+ // (2D texture for compatibility; used to be a 1D texture)
gl.glBindTexture(GL_TEXTURE_2D, m_colormapTex);
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);
-
- // The output texture
- gl.glGenTextures(1, &m_tex);
- gl.glBindTexture(GL_TEXTURE_2D, m_tex);
- 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_MAG_FILTER, GL_LINEAR);
- gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
-VoronoiSplatTexture::~VoronoiSplatTexture()
+VoronoiSplatRenderer::~VoronoiSplatRenderer()
{
gl.glDeleteBuffers(3, m_VBOs);
gl.glDeleteTextures(2, m_textures);
gl.glDeleteTextures(1, &m_colormapTex);
- gl.glDeleteTextures(1, &m_tex);
gl.glDeleteFramebuffers(1, &m_FBO);
@@ -207,18 +314,18 @@ VoronoiSplatTexture::~VoronoiSplatTexture()
delete m_program2;
}
-void VoronoiSplatTexture::bind()
+QOpenGLFramebufferObject *VoronoiSplatRenderer::createFramebufferObject(const QSize &size)
{
- gl.glBindTexture(GL_TEXTURE_2D, m_tex);
+ int baseSize = nextPow2(std::min(size.width(), size.height()));
+ m_size.setWidth(baseSize);
+ m_size.setHeight(baseSize);
+ resizeTextures();
+
+ return QQuickFramebufferObject::Renderer::createFramebufferObject(m_size);
}
-bool VoronoiSplatTexture::updateTexture()
+void VoronoiSplatRenderer::render()
{
- if (!m_sitesChanged && !m_valuesChanged && !m_colormapChanged) {
- // Texture is already updated
- return false;
- }
-
// Update OpenGL buffers and textures as needed
if (m_sitesChanged) {
updateSites();
@@ -230,6 +337,8 @@ bool VoronoiSplatTexture::updateTexture()
updateColormap();
}
+ int originalFBO;
+ gl.glGetIntegerv(GL_FRAMEBUFFER_BINDING, &originalFBO);
gl.glBindFramebuffer(GL_FRAMEBUFFER, m_FBO);
// First pass
@@ -256,7 +365,7 @@ bool VoronoiSplatTexture::updateTexture()
gl.glClear(GL_COLOR_BUFFER_BIT);
m_sitesVAO.bind();
- gl.glDrawArrays(GL_POINTS, 0, m_values.size());
+ gl.glDrawArrays(GL_POINTS, 0, m_values->size());
m_sitesVAO.release();
m_program1->release();
@@ -277,9 +386,8 @@ bool VoronoiSplatTexture::updateTexture()
gl.glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- // We now render to the output texture
- gl.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
- GL_TEXTURE_2D, m_tex, 0);
+ // We now render to the QQuickFramebufferObject's FBO
+ gl.glBindFramebuffer(GL_FRAMEBUFFER, originalFBO);
gl.glClearColor(0, 0, 0, 0);
gl.glClear(GL_COLOR_BUFFER_BIT);
@@ -290,14 +398,27 @@ bool VoronoiSplatTexture::updateTexture()
m_program2->release();
- return true;
+ m_window->resetOpenGLState();
}
-void VoronoiSplatTexture::updateSites()
+void VoronoiSplatRenderer::synchronize(QQuickFramebufferObject *item)
+{
+ VoronoiSplat *splat = static_cast<VoronoiSplat *>(item);
+
+ m_sitesChanged = splat->sitesChanged();
+ m_valuesChanged = splat->valuesChanged();
+ m_colormapChanged = splat->colormapChanged();
+ m_sites = &(splat->sites());
+ m_values = &(splat->values());
+ m_cmap = &(splat->colormap());
+ m_window = splat->window();
+}
+
+void VoronoiSplatRenderer::updateSites()
{
gl.glBindBuffer(GL_ARRAY_BUFFER, m_VBOs[0]);
- gl.glBufferData(GL_ARRAY_BUFFER, m_sites.size() * sizeof(float),
- m_sites.data(), GL_DYNAMIC_DRAW);
+ gl.glBufferData(GL_ARRAY_BUFFER, m_sites->size() * sizeof(float),
+ m_sites->data(), GL_DYNAMIC_DRAW);
// Compute DT values for the new positions
computeDT();
@@ -305,32 +426,33 @@ void VoronoiSplatTexture::updateSites()
m_sitesChanged = false;
}
-void VoronoiSplatTexture::updateValues()
+void VoronoiSplatRenderer::updateValues()
{
gl.glBindBuffer(GL_ARRAY_BUFFER, m_VBOs[1]);
- gl.glBufferData(GL_ARRAY_BUFFER, m_values.size() * sizeof(float),
- m_values.data(), GL_DYNAMIC_DRAW);
+ gl.glBufferData(GL_ARRAY_BUFFER, m_values->size() * sizeof(float),
+ m_values->data(), GL_DYNAMIC_DRAW);
m_valuesChanged = false;
}
-void VoronoiSplatTexture::updateColormap()
+void VoronoiSplatRenderer::updateColormap()
{
gl.glBindTexture(GL_TEXTURE_2D, m_colormapTex);
gl.glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, COLORMAP_SAMPLES, 1, GL_RGB,
- GL_FLOAT, m_cmap.data());
+ GL_FLOAT, m_cmap->data());
m_colormapChanged = false;
}
-void VoronoiSplatTexture::computeDT()
+void VoronoiSplatRenderer::computeDT()
{
int w = m_size.width(), h = m_size.height();
// Compute FT of the sites
std::vector<float> buf(w*h);
- for (unsigned i = 0; i < m_sites.size(); i += 2) {
- buf[int(m_sites[i + 1])*h + int(m_sites[i])] = (float) i/2 + 1;
+ 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;
}
skelft2DFT(0, buf.data(), 0, 0, w, h, w);
@@ -348,67 +470,3 @@ void VoronoiSplatTexture::computeDT()
dtTexData.data());
}
-void VoronoiSplatTexture::setSites(const arma::mat &points)
-{
- if (points.n_rows < 1 || points.n_cols != 2
- || (m_values.size() != 0 && points.n_rows != m_values.size())) {
- return;
- }
-
- // Copy 'points' to internal data structure(s)
- double minX = points.col(0).min();
- double maxX = points.col(0).max();
- double minY = points.col(1).min();
- double maxY = points.col(1).max();
-
- // Coords are packed into 'm_sites' as [ x1, y1, x2, y2, ... ]
- m_sites.resize(2*points.n_rows);
- LinearScale<float> sx(minX, maxX, PADDING, m_size.width() - 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, m_size.height() - PADDING, PADDING);
- for (unsigned i = 0; i < points.n_rows; i++) {
- m_sites[2*i + 1] = sy(col[i]);
- }
-
- m_sitesChanged = true;
-}
-
-void VoronoiSplatTexture::setValues(const arma::vec &values)
-{
- if (values.n_elem == 0
- || (m_sites.size() != 0 && values.n_elem != m_sites.size() / 2)) {
- return;
- }
-
- 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);
-
- m_valuesChanged = true;
-}
-
-void VoronoiSplatTexture::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;
- }
-
- m_colormapChanged = true;
-}
diff --git a/voronoisplat.h b/voronoisplat.h
index 9859e8d..fb93f09 100644
--- a/voronoisplat.h
+++ b/voronoisplat.h
@@ -1,63 +1,50 @@
#ifndef VORONOISPLAT_H
#define VORONOISPLAT_H
-#include <QSGDynamicTexture>
-#include <QOpenGLFunctions>
-#include <QOpenGLShaderProgram>
-#include <QOpenGLVertexArrayObject>
+#include <QQuickFramebufferObject>
+
#include <armadillo>
#include "colorscale.h"
-class VoronoiSplatTexture
- : public QSGDynamicTexture
+class VoronoiSplat
+ : public QQuickFramebufferObject
{
+ Q_OBJECT
public:
- // 'size' must be square (and power of 2)
- VoronoiSplatTexture(const QSize &size);
- virtual ~VoronoiSplatTexture();
+ VoronoiSplat(QQuickItem *parent = 0);
+
+ Renderer *createRenderer() const;
- void bind();
+ const std::vector<float> &sites() const { return m_sites; }
+ const std::vector<float> &values() const { return m_values; }
+ const std::vector<float> &colormap() const { return m_cmap; }
- // Call this whenever the texture should be updated (after changing the
- // sites, values or colormap). When true is returned, should probably call
- // resetOpenGLState() on QtQuick window
- bool updateTexture();
+ bool sitesChanged() const { return m_sitesChanged; }
+ bool valuesChanged() const { return m_valuesChanged; }
+ bool colormapChanged() const { return m_colormapChanged; }
- bool hasAlphaChannel() const { return true; }
- bool hasMipmaps() const { return false; }
- int textureId() const { return m_tex; }
- QSize textureSize() const { return m_size; }
+ void setSitesChanged(bool sitesChanged) { m_sitesChanged = sitesChanged; }
+ void setValuesChanged(bool valuesChanged) { m_valuesChanged = valuesChanged; }
+ void setColormapChanged(bool colormapChanged) { m_colormapChanged = colormapChanged; }
+signals:
+ void sitesChanged(const arma::mat &sites);
+ void valuesChanged(const arma::vec &values);
+ void colormapChanged(const ColorScale *scale);
+
+public slots:
// 'points' should be a 2D points matrix (each point in a row)
void setSites(const arma::mat &points);
// Set the value to be colormapped in each site
void setValues(const arma::vec &values);
- // Set colormap data based on the given color scale;
+ // Set colormap data based on the given color scale
void setColormap(const ColorScale *scale);
private:
- void setupShaders();
- void setupVAOs();
- void setupTextures();
-
- void updateSites();
- void updateValues();
- void updateColormap();
- void computeDT();
-
- QOpenGLFunctions gl;
- QOpenGLShaderProgram *m_program1, *m_program2;
- GLuint m_FBO;
- GLuint m_VBOs[3];
- GLuint m_textures[2], m_colormapTex, m_tex;
- QOpenGLVertexArrayObject m_sitesVAO, m_2ndPassVAO;
-
std::vector<float> m_sites, m_values, m_cmap;
- QSize m_size;
-
bool m_sitesChanged, m_valuesChanged, m_colormapChanged;
};