diff options
author | Samuel Fadel <samuelfadel@gmail.com> | 2016-02-28 21:46:31 -0300 |
---|---|---|
committer | Samuel Fadel <samuelfadel@gmail.com> | 2016-02-28 21:46:31 -0300 |
commit | d3d3526bab2ab051656a1b80b4e0f1337037b5de (patch) | |
tree | 1df83705676cfac7cc8f755d8025d05b1b1e43cd | |
parent | 5bd1fec462466d605f974fa27c3b00826a1bab57 (diff) |
Added colormap orientation & improved UI.
* Colormap: orientation now enables vertical & horizontal colormap
display
* Two colormap components, one for each type of point (regular,
control)
* Improved controls grouping and cohesion
-rw-r--r-- | colormap.cpp | 77 | ||||
-rw-r--r-- | colormap.h | 14 | ||||
-rw-r--r-- | main.cpp | 14 | ||||
-rw-r--r-- | main.h | 33 | ||||
-rw-r--r-- | main_view.qml | 269 |
5 files changed, 246 insertions, 161 deletions
diff --git a/colormap.cpp b/colormap.cpp index cc57048..3ebf5c8 100644 --- a/colormap.cpp +++ b/colormap.cpp @@ -1,5 +1,7 @@ #include "colormap.h" +#include <algorithm> + #include <QOpenGLFunctions> #include <QSGSimpleTextureNode> @@ -7,7 +9,8 @@ class ColormapTexture : public QSGDynamicTexture { public: - ColormapTexture(const std::vector<float> *cmap); + ColormapTexture(const std::vector<float> *cmap, + Colormap::Orientation orientation = Colormap::Horizontal); ~ColormapTexture(); bool hasAlphaChannel() const { return false; } @@ -18,26 +21,29 @@ public: int textureId() const { return m_texture; } QSize textureSize() const { return m_size; } + void setOrientation(Colormap::Orientation orientation); + private: QOpenGLFunctions gl; + Colormap::Orientation m_orientation; QSize m_size; GLuint m_texture; const std::vector<float> *m_cmap; }; -ColormapTexture::ColormapTexture(const std::vector<float> *cmap) +ColormapTexture::ColormapTexture(const std::vector<float> *cmap, + Colormap::Orientation orientation) : gl(QOpenGLContext::currentContext()) - , m_size(cmap->size() / 3, 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); + + setOrientation(orientation); } ColormapTexture::~ColormapTexture() @@ -50,9 +56,28 @@ void ColormapTexture::bind() gl.glBindTexture(GL_TEXTURE_2D, m_texture); } +void ColormapTexture::setOrientation(Colormap::Orientation orientation) +{ + if (m_orientation == orientation) { + return; + } + + m_orientation = orientation; + updateTexture(); +} + bool ColormapTexture::updateTexture() { - m_size.setWidth(m_cmap->size() / 3); + switch (m_orientation) { + case Colormap::Horizontal: + m_size.setWidth(m_cmap->size() / 3); + m_size.setHeight(1); + break; + case Colormap::Vertical: + m_size.setWidth(1); + m_size.setHeight(m_cmap->size() / 3); + break; + } gl.glBindTexture(GL_TEXTURE_2D, m_texture); gl.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, m_size.width(), m_size.height(), @@ -64,6 +89,7 @@ Colormap::Colormap(QQuickItem *parent) : QQuickItem(parent) , m_texture(0) , m_shouldUpdateTexture(false) + , m_orientation(Colormap::Horizontal) { setFlag(QQuickItem::ItemHasContents); } @@ -75,11 +101,42 @@ Colormap::~Colormap() } } +static void reverseCMap(std::vector<float> &cmap) +{ + decltype(cmap.size()) i = 0, j = cmap.size() - 3; + + while (i < j) { + std::swap(cmap[i++], cmap[j++]); + std::swap(cmap[i++], cmap[j++]); + std::swap(cmap[i++], cmap[j++]); + + j -= 6; + } +} + +void Colormap::setOrientation(Colormap::Orientation orientation) +{ + if (m_orientation == orientation) { + return; + } + + if (!m_cmap.empty()) { + reverseCMap(m_cmap); + } + + m_orientation = orientation; + m_shouldUpdateOrientation = true; + update(); +} void Colormap::setColorScale(const ColorScale &scale) { m_cmap.resize(scale.numColors() * 3); scale.sample(scale.numColors(), m_cmap.data()); + if (m_orientation == Colormap::Vertical) { + reverseCMap(m_cmap); + } + emit colorScaleChanged(scale); m_shouldUpdateTexture = true; @@ -107,8 +164,14 @@ QSGNode *Colormap::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) QSGSimpleTextureNode *node = static_cast<QSGSimpleTextureNode *>(root); node->setRect(x(), y(), width(), height()); + ColormapTexture *texture = static_cast<ColormapTexture *>(m_texture); + if (m_shouldUpdateOrientation) { + texture->setOrientation(m_orientation); + m_shouldUpdateOrientation = false; + } + if (m_shouldUpdateTexture) { - m_texture->updateTexture(); + texture->updateTexture(); m_shouldUpdateTexture = false; } @@ -14,14 +14,25 @@ class Colormap : public QQuickItem { Q_OBJECT + Q_ENUMS(Orientation) + Q_PROPERTY(Orientation orientation READ orientation WRITE setOrientation NOTIFY orientationChanged) public: static const int SAMPLES = 128; + enum Orientation { + Horizontal, + Vertical + }; + Colormap(QQuickItem *parent = 0); ~Colormap(); + void setOrientation(Orientation orientation); + Orientation orientation() const { return m_orientation; } + signals: void colorScaleChanged(const ColorScale &scale) const; + void orientationChanged(Orientation orientation) const; public slots: void setColorScale(const ColorScale &scale); @@ -33,7 +44,8 @@ private: QSGNode *newSceneGraph(); QSGDynamicTexture *m_texture; - bool m_shouldUpdateTexture; + bool m_shouldUpdateTexture, m_shouldUpdateOrientation; + Orientation m_orientation; std::vector<float> m_cmap; }; @@ -151,7 +151,8 @@ int main(int argc, char **argv) // Initialize pointers to visual components m->cpPlot = engine.rootObjects()[0]->findChild<Scatterplot *>("cpPlot"); m->rpPlot = engine.rootObjects()[0]->findChild<Scatterplot *>("rpPlot"); - m->colormap = engine.rootObjects()[0]->findChild<Colormap *>("colormap"); + m->cpColormap = engine.rootObjects()[0]->findChild<Colormap *>("cpColormap"); + m->rpColormap = engine.rootObjects()[0]->findChild<Colormap *>("rpColormap"); m->splat = engine.rootObjects()[0]->findChild<VoronoiSplat *>("splat"); m->cpBarChart = engine.rootObjects()[0]->findChild<BarChart *>("cpBarChart"); m->rpBarChart = engine.rootObjects()[0]->findChild<BarChart *>("rpBarChart"); @@ -193,9 +194,6 @@ int main(int argc, char **argv) QObject::connect(m->projectionHistory, &ProjectionHistory::currentMapChanged, &mapScaleHandler, &MapScaleHandler::scaleToMap); - QObject::connect(m->splat, &VoronoiSplat::colorScaleChanged, - m->colormap, &Colormap::setColorScale); - // Linking between selections SelectionHandler cpSelectionHandler(cpIndices.n_elem); QObject::connect(m->cpPlot, &Scatterplot::selectionInteractivelyChanged, @@ -276,12 +274,8 @@ int main(int argc, char **argv) m->cpBarChart->setAcceptedMouseButtons(Qt::LeftButton | Qt::RightButton); m->rpBarChart->setAcceptedMouseButtons(Qt::LeftButton | Qt::RightButton); - m->setColormapColorScale(Main::ColorScaleRainbow); - m->setCPPlotColorScale(Main::ColorScaleRainbow); - m->setRPPlotColorScale(Main::ColorScaleRainbow); - m->setSplatColorScale(Main::ColorScaleRainbow); - m->setCPBarChartColorScale(Main::ColorScaleRainbow); - m->setRPBarChartColorScale(Main::ColorScaleRainbow); + m->setCPColorScale(Main::ColorScaleRainbow); + m->setRPColorScale(Main::ColorScaleRainbow); // This sets the initial CP configuration, triggering all the necessary // signals to set up the helper objects and visual components @@ -88,34 +88,27 @@ public: ColorScale COLOR_SCALE_DIVERGENT; ColorScale COLOR_SCALE_RAINBOW; - Q_INVOKABLE void setCPPlotColorScale(ColorScaleType colorScaleType) { - cpPlot->setColorScale(getColorScale(colorScaleType)); - } - - Q_INVOKABLE void setRPPlotColorScale(ColorScaleType colorScaleType) { - rpPlot->setColorScale(getColorScale(colorScaleType)); - } + Q_INVOKABLE void setCPColorScale(ColorScaleType colorScaleType) { + ColorScale &scale = getColorScale(colorScaleType); - Q_INVOKABLE void setColormapColorScale(ColorScaleType colorScaleType) { - colormap->setColorScale(getColorScale(colorScaleType)); + cpPlot->setColorScale(scale); + cpBarChart->setColorScale(scale); + cpColormap->setColorScale(scale); } - Q_INVOKABLE void setCPBarChartColorScale(ColorScaleType colorScaleType) { - cpBarChart->setColorScale(getColorScale(colorScaleType)); - } - - Q_INVOKABLE void setRPBarChartColorScale(ColorScaleType colorScaleType) { - rpBarChart->setColorScale(getColorScale(colorScaleType)); - } + Q_INVOKABLE void setRPColorScale(ColorScaleType colorScaleType) { + ColorScale &scale = getColorScale(colorScaleType); - Q_INVOKABLE void setSplatColorScale(ColorScaleType colorScaleType) { - splat->setColorScale(getColorScale(colorScaleType)); + rpPlot->setColorScale(scale); + splat->setColorScale(scale); + rpBarChart->setColorScale(scale); + rpColormap->setColorScale(scale); } // Pointers to visual components whose values are set in the main() function // after components are instantiated by the QtQuick engine BarChart *cpBarChart, *rpBarChart; - Colormap *colormap; + Colormap *cpColormap, *rpColormap; Scatterplot *cpPlot, *rpPlot; VoronoiSplat *splat; @@ -184,6 +177,8 @@ private: , COLOR_SCALE_RAINBOW{ContinuousColorScale::builtin(ContinuousColorScale::Rainbow)} , cpBarChart(0) , rpBarChart(0) + , cpColormap(0) + , rpColormap(0) , cpPlot(0) , rpPlot(0) , splat(0) diff --git a/main_view.qml b/main_view.qml index fd4bd4a..66fdf44 100644 --- a/main_view.qml +++ b/main_view.qml @@ -97,26 +97,6 @@ ApplicationWindow { anchors.fill: parent } - Colormap { - id: colormap - objectName: "colormap" - x: parent.x + 5 - y: parent.y + 5 - z: 2 - width: 128 - height: 5 - - Rectangle { // Adds a border around the colormap - x: parent.x - 1 - y: parent.y - 1 - width: parent.width + 2 - height: parent.height + 2 - border.width: 1 - border.color: "#000000" - color: "transparent" - } - } - TransitionControl { id: plotTC objectName: "plotTC" @@ -135,79 +115,114 @@ ApplicationWindow { } } - Rectangle { + RowLayout { Layout.minimumHeight: 60 Layout.fillHeight: true width: mainView.width - color: "#ffffff" - Item { - id: bottomViewCP - anchors.fill: parent + Colormap { + Layout.fillHeight: true + id: cpColormap + objectName: "cpColormap" + width: 5 + orientation: Colormap.Vertical + + Rectangle { // Adds a border around the colormap + x: parent.x - 1 + y: parent.y - 1 + width: parent.width + 2 + height: parent.height + 2 + border.width: 1 + border.color: "#000000" + color: "transparent" + } + } - BarChart { - id: cpBarChart - objectName: "cpBarChart" + Rectangle { + Layout.fillHeight: true + Layout.fillWidth: true + color: "#ffffff" + + Item { + id: bottomViewCP anchors.fill: parent - Label { + BarChart { + id: cpBarChart + objectName: "cpBarChart" anchors.fill: parent - anchors.margins: 5 - horizontalAlignment: Text.AlignRight - text: "Control points" + + Label { + anchors.fill: parent + anchors.margins: 5 + horizontalAlignment: Text.AlignRight + text: "Control points" + } } } - //HistoryGraph { - // id: history - // objectName: "history" - // anchors.fill: parent - //} - } - - Rectangle { - anchors.fill: parent - border.width: 1 - border.color: "#cccccc" - color: "transparent" + Rectangle { + anchors.fill: parent + border.width: 1 + border.color: "#cccccc" + color: "transparent" + } } } - Rectangle { + RowLayout { Layout.minimumHeight: 60 Layout.fillHeight: true width: mainView.width - color: "#ffffff" - Item { - id: bottomViewRP - anchors.fill: parent + Colormap { + Layout.fillHeight: true + id: rpColormap + objectName: "rpColormap" + width: 5 + orientation: Colormap.Vertical + + Rectangle { // Adds a border around the colormap + x: parent.x - 1 + y: parent.y - 1 + width: parent.width + 2 + height: parent.height + 2 + border.width: 1 + border.color: "#000000" + color: "transparent" + } + } + + Rectangle { + Layout.minimumHeight: 60 + Layout.fillHeight: true + Layout.fillWidth: true + color: "#ffffff" - BarChart { - id: rpBarChart - objectName: "rpBarChart" + Item { + id: bottomViewRP anchors.fill: parent - Label { + BarChart { + id: rpBarChart + objectName: "rpBarChart" anchors.fill: parent - anchors.margins: 5 - horizontalAlignment: Text.AlignRight - text: "Regular points" + + Label { + anchors.fill: parent + anchors.margins: 5 + horizontalAlignment: Text.AlignRight + text: "Regular points" + } } } - //HistoryGraph { - // id: history - // objectName: "history" - // anchors.fill: parent - //} - } - - Rectangle { - anchors.fill: parent - border.width: 1 - border.color: "#cccccc" - color: "transparent" + Rectangle { + anchors.fill: parent + border.width: 1 + border.color: "#cccccc" + color: "transparent" + } } } } @@ -232,11 +247,8 @@ ApplicationWindow { } } - GridLayout { - columns: 2 - + ColumnLayout { GroupBox { - Layout.columnSpan: 2 flat: true title: "Colors" @@ -253,34 +265,41 @@ ApplicationWindow { ComboBox { id: cpPlotColormapCombo model: colormapModel - onActivated: { - Main.setCPPlotColorScale(model.get(index).value); - Main.setCPBarChartColorScale(model.get(index).value); - } + onActivated: + Main.setCPColorScale(model.get(index).value); } } } - Label { text: "Glyph size:" } - SpinBox { - id: cpGlyphSizeSpinBox - maximumValue: 100 - minimumValue: 6 - decimals: 1 - stepSize: 1 - value: cpPlot.glyphSize - onValueChanged: cpPlot.glyphSize = this.value - } + GroupBox { + flat: true + title: "Glyphs" - Label { text: "Opacity:" } - Slider { - id: cpPlotOpacitySlider - tickmarksEnabled: true - stepSize: 0.1 - maximumValue: 1 - minimumValue: 0 - value: cpPlot.opacity - onValueChanged: cpPlot.opacity = this.value + GridLayout { + columns: 2 + + Label { text: "Size:" } + SpinBox { + id: cpGlyphSizeSpinBox + maximumValue: 100 + minimumValue: 6 + decimals: 1 + stepSize: 1 + value: cpPlot.glyphSize + onValueChanged: cpPlot.glyphSize = this.value + } + + Label { text: "Opacity:" } + Slider { + id: cpPlotOpacitySlider + tickmarksEnabled: true + stepSize: 0.1 + maximumValue: 1 + minimumValue: 0 + value: cpPlot.opacity + onValueChanged: cpPlot.opacity = this.value + } + } } } } @@ -295,11 +314,8 @@ ApplicationWindow { splat.visible = this.checked; } - GridLayout { - columns: 2 - + ColumnLayout { GroupBox { - Layout.columnSpan: 2 flat: true title: "Colors" @@ -316,17 +332,13 @@ ApplicationWindow { ComboBox { id: rpPlotColormapCombo model: colormapModel - onActivated: { - Main.setRPPlotColorScale(model.get(index).value); - Main.setSplatColorScale(model.get(index).value); - Main.setRPBarChartColorScale(model.get(index).value); - } + onActivated: + Main.setRPColorScale(model.get(index).value); } } } GroupBox { - Layout.columnSpan: 2 flat: true title: "Splat" @@ -368,26 +380,35 @@ ApplicationWindow { } } - Label { text: "Glyph size:" } - SpinBox { - id: rpGlyphSizeSpinBox - maximumValue: 100 - minimumValue: 2 - decimals: 1 - stepSize: 1 - value: rpPlot.glyphSize - onValueChanged: rpPlot.glyphSize = this.value - } + GroupBox { + flat: true + title: "Glyphs" + + GridLayout { + columns: 2 + + Label { text: "Size:" } + SpinBox { + id: rpGlyphSizeSpinBox + maximumValue: 100 + minimumValue: 2 + decimals: 1 + stepSize: 1 + value: rpPlot.glyphSize + onValueChanged: rpPlot.glyphSize = this.value + } - Label { text: "Opacity:" } - Slider { - id: rpPlotOpacitySlider - tickmarksEnabled: true - stepSize: 0.1 - maximumValue: 1 - minimumValue: 0 - value: rpPlot.opacity - onValueChanged: rpPlot.opacity = this.value + Label { text: "Opacity:" } + Slider { + id: rpPlotOpacitySlider + tickmarksEnabled: true + stepSize: 0.1 + maximumValue: 1 + minimumValue: 0 + value: rpPlot.opacity + onValueChanged: rpPlot.opacity = this.value + } + } } } } |