From 7ad4b526899f8bc764192e88e65306c788a9ed32 Mon Sep 17 00:00:00 2001 From: Samuel Fadel Date: Fri, 15 Jan 2016 22:35:13 +0100 Subject: 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. --- main.cpp | 37 ++++---- main_view.qml | 8 ++ scatterplot.cpp | 71 -------------- scatterplot.h | 16 ++-- voronoisplat.cpp | 278 +++++++++++++++++++++++++++++++++---------------------- voronoisplat.h | 61 +++++------- 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("PM", 1, 0, "Scatterplot"); qmlRegisterType("PM", 1, 0, "HistoryGraph"); qmlRegisterType("PM", 1, 0, "BarChart"); + qmlRegisterType("PM", 1, 0, "VoronoiSplat"); qmlRegisterType("PM", 1, 0, "InteractionHandler"); qmlRegisterSingletonType
("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("cpPlot"); - cpPlot->setDisplaySplat(false); cpPlot->setAcceptedMouseButtons(Qt::LeftButton | Qt::MiddleButton | Qt::RightButton); // cpPlot->setColorData(arma::zeros(cpSize)); cpPlot->setColorScale(&colorScale); Scatterplot *plot = engine.rootObjects()[0]->findChild("plot"); - skelft2DInitialization(plot->width()); + VoronoiSplat *splat = engine.rootObjects()[0]->findChild("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 @@ -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(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(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 &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 -#include #include #include -#include + +#include #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 &selection) const; - void displaySplatChanged(bool displaySplat) const; void scaleChanged(const LinearScale &sx, const LinearScale &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 &selection); - void setDisplaySplat(bool displaySplat); void setScale(const LinearScale &sx, const LinearScale &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 #include -#include +#include +#include +#include #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 *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 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 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 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(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 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 &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 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 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 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 -#include -#include -#include +#include + #include #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 &sites() const { return m_sites; } + const std::vector &values() const { return m_values; } + const std::vector &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 m_sites, m_values, m_cmap; - QSize m_size; - bool m_sitesChanged, m_valuesChanged, m_colormapChanged; }; -- cgit v1.2.3