aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSamuel Fadel <samuelfadel@gmail.com>2016-01-17 16:09:51 +0100
committerSamuel Fadel <samuelfadel@gmail.com>2016-01-17 16:09:51 +0100
commita9236429e5691159f1ddc017b28ee0c060e0092d (patch)
tree73abfac910e8b801105190e74b8fe3251c4c44bf
parent2260996d93e723a55a72bf23447a8f77e98e1371 (diff)
Added a options panel.
* Added screenshot action that saves two images: one of the main view (plot + splat) and one of the bottom view (bar chart) * Added methods/signals/slots to Scatterplot for handling glyph sizes * Added methods/signals/slots to VoronoiSplat for handling the alpha/beta parameters, which are now also no longer fixed * Options panel: - glyph sizes of both CPs and RPs - splat opacity - splat parameters (alpha & beta) - color scale combo box currently does nothing
-rw-r--r--main.cpp34
-rw-r--r--main_view.qml249
-rw-r--r--scatterplot.cpp44
-rw-r--r--scatterplot.h26
-rw-r--r--voronoisplat.cpp26
-rw-r--r--voronoisplat.h18
6 files changed, 280 insertions, 117 deletions
diff --git a/main.cpp b/main.cpp
index b79f6ff..402ecef 100644
--- a/main.cpp
+++ b/main.cpp
@@ -78,6 +78,8 @@ int main(int argc, char **argv)
// cpIndices = relevanceSampling(X, cpSize);
cpIndices = arma::randi<arma::uvec>(cpSize, arma::distr_param(0, n-1));
}
+
+ arma::sort(cpIndices);
}
if (parser.isSet(cpFileOutputOption)) {
const QString &cpFilename = parser.value(cpFileOutputOption);
@@ -115,22 +117,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.setExtents(labels.min(), labels.max());
-
- //ContinuousColorScale colorScale = ContinuousColorScale::builtin(ContinuousColorScale::RED_GRAY_BLUE);
+ //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::RAINBOW);
+ 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));
@@ -177,13 +179,13 @@ int main(int argc, char **argv)
cpPlot, SLOT(setScale(const LinearScale<float> &, const LinearScale<float> &)));
BarChart *barChart = engine.rootObjects()[0]->findChild<BarChart *>("barChart");
- barChart->setValues(arma::randn<arma::vec>(100));
+ barChart->setValues(labels);
//history->addHistoryItem(Ys);
colormap->setColorScale(colorScale);
plot->setColorScale(&colorScale);
plot->setColorData(labels, false);
- //splat->setColormap(colorScale);
+ splat->setColorScale(colorScale);
splat->setValues(labels);
cpPlot->setAutoScale(false);
diff --git a/main_view.qml b/main_view.qml
index 68d6fea..544f7dc 100644
--- a/main_view.qml
+++ b/main_view.qml
@@ -10,10 +10,8 @@ ApplicationWindow {
id: mainWindow
title: "Projection"
visible: true
- contentItem.minimumWidth: 532
+ contentItem.minimumWidth: 800
contentItem.minimumHeight: 622
- contentItem.maximumWidth: contentItem.minimumWidth
- contentItem.maximumHeight: contentItem.minimumHeight
Component.onCompleted: {
setX(Screen.width / 2 - width / 2);
setY(Screen.height / 2 - height / 2);
@@ -23,6 +21,7 @@ ApplicationWindow {
Menu {
title: "File"
MenuItem { action: savePlotAction }
+ MenuItem { action: screenshotAction }
MenuItem { action: quitAction }
}
@@ -89,78 +88,193 @@ ApplicationWindow {
}
}
- ColumnLayout {
- spacing: 10
+ GridLayout {
anchors.fill: parent
- anchors.margins: this.spacing
-
- Rectangle {
- //Layout.fillWidth: true
- //Layout.fillHeight: true
- width: 512
- height: 512
- border.width: 1
- border.color: "#cccccc"
-
- VoronoiSplat {
- id: splat
- objectName: "splat"
- x: parent.x
- y: parent.y
- anchors.fill: parent
- }
+ anchors.margins: 10
- Scatterplot {
- id: plot
- objectName: "plot"
- x: parent.x
- y: parent.y
- anchors.fill: parent
- }
+ // Main panel
+ ColumnLayout {
+ Rectangle {
+ width: 512
+ height: 512
+ border.width: 1
+ border.color: "#cccccc"
+
+ Item {
+ id: mainView
+ anchors.fill: parent
+ VoronoiSplat {
+ id: splat
+ objectName: "splat"
+ x: parent.x
+ y: parent.y
+ anchors.fill: parent
+ }
+
+ Scatterplot {
+ id: plot
+ objectName: "plot"
+ x: parent.x
+ y: parent.y
+ anchors.fill: parent
+ }
+
+ Scatterplot {
+ id: cpPlot
+ objectName: "cpPlot"
+ x: parent.x
+ y: parent.y
+ anchors.fill: parent
+ }
- Scatterplot {
- id: cpPlot
- objectName: "cpPlot"
- x: parent.x
- y: parent.y
- anchors.fill: parent
+ Colormap {
+ id: colormap
+ objectName: "colormap"
+ x: parent.x + 5
+ y: parent.y + 5
+ width: 128
+ height: 10
+ }
+ }
}
- Colormap {
- id: colormap
- objectName: "colormap"
- x: parent.x + 5
- y: parent.y + 5
- width: 128
- height: 10
+ Rectangle {
+ Layout.minimumHeight: 80
+ width: mainView.width
+ border.width: 1
+ border.color: "#cccccc"
+
+ Item {
+ id: bottomView
+ anchors.fill: parent
+ BarChart {
+ id: barChart
+ objectName: "barChart"
+ anchors.fill: parent
+ }
+
+ //HistoryGraph {
+ // id: history
+ // objectName: "history"
+ // anchors.fill: parent
+ //}
+ }
}
}
- Rectangle {
- Layout.fillWidth: true
- Layout.minimumHeight: 80
- border.width: 1
- border.color: "#cccccc"
+ // Options panel
+ RowLayout {
+ anchors.margins: parent.anchors.margins
+
+ // Left column
+ ColumnLayout {
+ GroupBox {
+ title: "Scatterplot"
+
+ ColumnLayout {
+ GridLayout {
+ columns: 2
+
+ Label { text: "Colors:" }
+ ComboBox {
+ id: colormapCombo
+ model: [ "Categorical", "Continuous", "Divergent", "Rainbow" ]
+ }
+ }
+
+ GroupBox {
+ flat: true
+ title: "Glyph size"
+
+ GridLayout {
+ columns: 2
+ Label { text: "Control points:" }
+ SpinBox {
+ id: cpGlyphSizeSpinBox
+ maximumValue: 50
+ minimumValue: 8
+ value: cpPlot.glyphSize()
+ decimals: 1
+ stepSize: 1
+ onValueChanged: cpPlot.setGlyphSize(this.value)
+ }
+
+ Label { text: "Regular points:" }
+ SpinBox {
+ id: rpGlyphSizeSpinBox
+ maximumValue: 50
+ minimumValue: 8
+ value: plot.glyphSize()
+ decimals: 1
+ stepSize: 1
+ onValueChanged: plot.setGlyphSize(this.value)
+ }
+ }
+ }
+ }
+ }
+
+ GroupBox {
+ title: "Splat"
+
+ GridLayout {
+ columns: 2
+
+ Label { text: "Alpha:" }
+ SpinBox {
+ id: alphaSpinBox
+ maximumValue: 100
+ minimumValue: 1
+ value: splat.alpha()
+ decimals: 2
+ stepSize: 1
+ onValueChanged: splat.setAlpha(this.value)
+ }
- BarChart {
- id: barChart
- objectName: "barChart"
- anchors.fill: parent
+ Label { text: "Beta:" }
+ SpinBox {
+ id: betaSpinBox
+ maximumValue: 100
+ minimumValue: 1
+ value: splat.beta()
+ decimals: 2
+ stepSize: 1
+ onValueChanged: splat.setBeta(this.value)
+ }
+
+ Label { text: "Opacity (%):" }
+ SpinBox {
+ id: splatOpacitySpinBox
+ maximumValue: 100
+ minimumValue: 0
+ value: 100 * splat.opacity
+ decimals: 0
+ stepSize: 1
+ onValueChanged: splat.opacity = this.value / 100
+ }
+ }
+ }
}
- //HistoryGraph {
- // id: history
- // objectName: "history"
- // anchors.fill: parent
- //}
+ // Right column
+ ColumnLayout {
+ }
}
}
Action {
- id: quitAction
- text: "&Quit"
- shortcut: "Ctrl+Q"
- onTriggered: Qt.quit()
+ id: screenshotAction
+ text: "Save screenshot"
+ shortcut: "Ctrl+Shift+S"
+ onTriggered: {
+ mainView.grabToImage(function(result) {
+ result.saveToFile("screenshot-main.png");
+ });
+
+ bottomView.grabToImage(function(result) {
+ result.saveToFile("screenshot-bottom.png");
+ });
+ }
}
Action {
@@ -173,6 +287,13 @@ ApplicationWindow {
}
}
+ Action {
+ id: quitAction
+ text: "&Quit"
+ shortcut: "Ctrl+Q"
+ onTriggered: Qt.quit()
+ }
+
ExclusiveGroup {
id: techniqueGroup
@@ -269,12 +390,4 @@ ApplicationWindow {
onTriggered: console.log("stub: Silhouette")
}
}
-
- // TODO
- //Window {
- // title: "Options"
- // minimumWidth: 500
- // minimumHeight: 300
- // visible: false
- //}
}
diff --git a/scatterplot.cpp b/scatterplot.cpp
index 8f08fee..5e67dc3 100644
--- a/scatterplot.cpp
+++ b/scatterplot.cpp
@@ -10,11 +10,12 @@ static const QColor GLYPH_OUTLINE_COLOR(255, 255, 255);
static const QColor GLYPH_OUTLINE_COLOR_SELECTED(0, 0, 0);
static const QColor SELECTION_COLOR(128, 128, 128, 96);
-static const float GLYPH_SIZE = 8.0f;
+static const float DEFAULT_GLYPH_SIZE = 8.0f;
static const float GLYPH_OUTLINE_WIDTH = 2.0f;
Scatterplot::Scatterplot(QQuickItem *parent)
: QQuickItem(parent)
+ , m_glyphSize(DEFAULT_GLYPH_SIZE)
, m_autoScale(true)
, m_sx(0, 1, 0, 1)
, m_sy(0, 1, 0, 1)
@@ -76,7 +77,6 @@ void Scatterplot::setXY(const arma::mat &xy, bool updateView)
}
m_shouldUpdateGeometry = true;
-
if (updateView) {
update();
}
@@ -97,7 +97,6 @@ void Scatterplot::setColorData(const arma::vec &colorData, bool updateView)
emit colorDataChanged(m_colorData);
m_shouldUpdateMaterials = true;
-
if (updateView) {
update();
}
@@ -108,14 +107,6 @@ void Scatterplot::setColorData(const arma::vec &colorData)
setColorData(colorData, true);
}
-void Scatterplot::setAutoScale(bool autoScale)
-{
- m_autoScale = autoScale;
- if (autoScale) {
- this->autoScale();
- }
-}
-
void Scatterplot::setOpacityData(const arma::vec &opacityData, bool updateView)
{
if (m_xy.n_rows > 0 && opacityData.n_elem != m_xy.n_rows) {
@@ -142,7 +133,6 @@ void Scatterplot::setScale(const LinearScale<float> &sx, const LinearScale<float
emit scaleChanged(m_sx, m_sy);
m_shouldUpdateGeometry = true;
-
if (updateView) {
update();
}
@@ -153,6 +143,14 @@ void Scatterplot::setScale(const LinearScale<float> &sx, const LinearScale<float
setScale(sx, sy, true);
}
+void Scatterplot::setAutoScale(bool autoScale)
+{
+ m_autoScale = autoScale;
+ if (autoScale) {
+ this->autoScale();
+ }
+}
+
void Scatterplot::autoScale()
{
m_sx.setDomain(m_xy.col(0).min(), m_xy.col(0).max());
@@ -161,6 +159,22 @@ void Scatterplot::autoScale()
emit scaleChanged(m_sx, m_sy);
}
+void Scatterplot::setGlyphSize(float glyphSize, bool updateView)
+{
+ m_glyphSize = glyphSize;
+ emit glyphSizeChanged(m_glyphSize);
+
+ m_shouldUpdateGeometry = true;
+ if (updateView) {
+ update();
+ }
+}
+
+void Scatterplot::setGlyphSize(float glyphSize)
+{
+ setGlyphSize(glyphSize, true);
+}
+
QSGNode *Scatterplot::newGlyphTree()
{
// NOTE:
@@ -171,7 +185,7 @@ QSGNode *Scatterplot::newGlyphTree()
}
QSGNode *node = new QSGNode;
- int vertexCount = calculateCircleVertexCount(GLYPH_SIZE);
+ int vertexCount = calculateCircleVertexCount(m_glyphSize);
for (arma::uword i = 0; i < m_xy.n_rows; i++) {
QSGGeometry *glyphOutlineGeometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), vertexCount);
@@ -293,11 +307,11 @@ void Scatterplot::updateGlyphs(QSGNode *glyphsNode)
y = m_sy(row[1]) + ty * moveTranslationF;
QSGGeometry *geometry = glyphOutlineNode->geometry();
- updateCircleGeometry(geometry, GLYPH_SIZE, x, y);
+ updateCircleGeometry(geometry, m_glyphSize, x, y);
glyphOutlineNode->markDirty(QSGNode::DirtyGeometry);
geometry = glyphNode->geometry();
- updateCircleGeometry(geometry, GLYPH_SIZE - 2*GLYPH_OUTLINE_WIDTH, x, y);
+ updateCircleGeometry(geometry, m_glyphSize - 2*GLYPH_OUTLINE_WIDTH, x, y);
glyphNode->markDirty(QSGNode::DirtyGeometry);
}
if (m_shouldUpdateMaterials) {
diff --git a/scatterplot.h b/scatterplot.h
index 2997425..3c2d50b 100644
--- a/scatterplot.h
+++ b/scatterplot.h
@@ -22,9 +22,12 @@ public:
void setColorData(const arma::vec &colorData, bool updateView);
void setOpacityData(const arma::vec &opacityData, bool updateView);
void setScale(const LinearScale<float> &sx, const LinearScale<float> &sy, bool updateView);
+ void setGlyphSize(float glyphSize, bool updateView);
void setAutoScale(bool autoScale);
Q_INVOKABLE bool saveToFile(const QUrl &url);
+ Q_INVOKABLE float glyphSize() const { return m_glyphSize; }
+
static const int PADDING = 10;
signals:
@@ -34,6 +37,7 @@ signals:
void opacityDataChanged(const arma::vec &opacityData) const;
void selectionChanged(const QSet<int> &selection) const;
void scaleChanged(const LinearScale<float> &sx, const LinearScale<float> &sy) const;
+ void glyphSizeChanged(float glyphSize) const;
public slots:
void setXY(const arma::mat &xy);
@@ -41,6 +45,7 @@ public slots:
void setOpacityData(const arma::vec &opacityData);
void setSelection(const QSet<int> &selection);
void setScale(const LinearScale<float> &sx, const LinearScale<float> &sy);
+ Q_INVOKABLE void setGlyphSize(float glyphSize);
protected:
QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *);
@@ -53,17 +58,25 @@ private:
QSGNode *newGlyphTree();
void applyManipulation();
-
void updateGlyphs(QSGNode *node);
- bool updateSelection(bool mergeSelection);
-
+ // Data
arma::mat m_xy;
+ arma::vec m_colorData;
+ arma::vec m_opacityData;
+
+ // Visuals
+ float m_glyphSize;
+ const ColorScale *m_colorScale;
void autoScale();
bool m_autoScale;
LinearScale<float> m_sx, m_sy;
+ // Internal state
+ bool updateSelection(bool mergeSelection);
+ QSet<int> m_selectedGlyphs;
+
enum InteractionState {
INTERACTION_NONE,
INTERACTION_SELECTING,
@@ -74,14 +87,7 @@ private:
QPointF m_dragOriginPos, m_dragCurrentPos;
- QSet<int> m_selectedGlyphs;
-
bool m_shouldUpdateGeometry, m_shouldUpdateMaterials;
-
- const ColorScale *m_colorScale;
-
- arma::vec m_colorData;
- arma::vec m_opacityData;
};
#endif // SCATTERPLOT_H
diff --git a/voronoisplat.cpp b/voronoisplat.cpp
index dcddc11..9154c5e 100644
--- a/voronoisplat.cpp
+++ b/voronoisplat.cpp
@@ -12,7 +12,8 @@
#include "scatterplot.h"
#include "skelft.h"
-static const float RAD_BLUR = 5.0f;
+static const float DEFAULT_ALPHA = 5.0f;
+static const float DEFAULT_BETA = 20.0f;
static int nextPow2(int n)
{
@@ -27,6 +28,8 @@ static int nextPow2(int n)
VoronoiSplat::VoronoiSplat(QQuickItem *parent)
: QQuickFramebufferObject(parent)
, m_cmap(3*Colormap::SAMPLES)
+ , m_alpha(DEFAULT_ALPHA)
+ , m_beta(DEFAULT_BETA)
{
setTextureFollowsItemSize(false);
}
@@ -87,6 +90,18 @@ void VoronoiSplat::setColorScale(const ColorScale &scale)
update();
}
+void VoronoiSplat::setAlpha(float alpha)
+{
+ m_alpha = alpha;
+ update();
+}
+
+void VoronoiSplat::setBeta(float beta)
+{
+ m_beta = beta;
+ update();
+}
+
// ----------------------------------------------------------------------------
class VoronoiSplatRenderer
@@ -115,6 +130,7 @@ private:
QSize m_size;
const std::vector<float> *m_sites, *m_values, *m_cmap;
+ float m_alpha, m_beta;
QQuickWindow *m_window; // used to reset OpenGL state (as per docs)
QOpenGLFunctions gl;
@@ -336,8 +352,8 @@ void VoronoiSplatRenderer::render()
// First pass
m_program1->bind();
- m_program1->setUniformValue("rad_max", 20.0f);
- m_program1->setUniformValue("rad_blur", RAD_BLUR);
+ m_program1->setUniformValue("rad_max", m_beta);
+ m_program1->setUniformValue("rad_blur", m_alpha);
m_program1->setUniformValue("fb_size", float(m_size.width()));
gl.glActiveTexture(GL_TEXTURE0);
@@ -368,7 +384,7 @@ void VoronoiSplatRenderer::render()
// Second pass
m_program2->bind();
- m_program2->setUniformValue("rad_max", 20.0f);
+ m_program2->setUniformValue("rad_max", m_beta);
gl.glActiveTexture(GL_TEXTURE0);
gl.glBindTexture(GL_TEXTURE_2D, m_textures[0]);
@@ -410,6 +426,8 @@ void VoronoiSplatRenderer::synchronize(QQuickFramebufferObject *item)
splat->setValuesChanged(false);
splat->setColorScaleChanged(false);
+ m_alpha = splat->alpha();
+ m_beta = splat->beta();
m_sites = &(splat->sites());
m_values = &(splat->values());
m_cmap = &(splat->colorScale());
diff --git a/voronoisplat.h b/voronoisplat.h
index f334261..6682fed 100644
--- a/voronoisplat.h
+++ b/voronoisplat.h
@@ -16,12 +16,14 @@ public:
Renderer *createRenderer() const;
- const std::vector<float> &sites() const { return m_sites; }
- const std::vector<float> &values() const { return m_values; }
+ const std::vector<float> &sites() const { return m_sites; }
+ const std::vector<float> &values() const { return m_values; }
const std::vector<float> &colorScale() const { return m_cmap; }
+ Q_INVOKABLE float alpha() const { return m_alpha; }
+ Q_INVOKABLE float beta() const { return m_beta; }
- bool sitesChanged() const { return m_sitesChanged; }
- bool valuesChanged() const { return m_valuesChanged; }
+ bool sitesChanged() const { return m_sitesChanged; }
+ bool valuesChanged() const { return m_valuesChanged; }
bool colorScaleChanged() const { return m_colorScaleChanged; }
void setSitesChanged(bool sitesChanged) {
@@ -38,6 +40,7 @@ signals:
void sitesChanged(const arma::mat &sites) const;
void valuesChanged(const arma::vec &values) const;
void colorScaleChanged(const ColorScale &scale) const;
+ void alphaChanged(float alpha) const;
public slots:
// 'points' should be a 2D points matrix (each point in a row)
@@ -49,8 +52,15 @@ public slots:
// Set colorScale data based on the given color scale
void setColorScale(const ColorScale &scale);
+ // Shepard blur radius
+ Q_INVOKABLE void setAlpha(float alpha);
+
+ // Maximum blur radius
+ Q_INVOKABLE void setBeta(float beta);
+
private:
std::vector<float> m_sites, m_values, m_cmap;
+ float m_alpha, m_beta;
bool m_sitesChanged, m_valuesChanged, m_colorScaleChanged;
};