From aafcab1e7237d3ccbc80945eb8379a985d567a53 Mon Sep 17 00:00:00 2001 From: Samuel Fadel Date: Mon, 18 May 2015 13:10:34 -0300 Subject: ForceScheme now (hopefully) does not produce NaNs. Removed the Glyph class and placed all scatterplot drawing code inside the Scatterplot class. --- forceScheme.cpp | 13 ++++--- glyph.cpp | 103 -------------------------------------------------------- glyph.h | 42 ----------------------- main.cpp | 1 - mp.h | 2 +- pm.pro | 4 +-- scatterplot.cpp | 94 +++++++++++++++++++++++++++++++++++++++++---------- 7 files changed, 85 insertions(+), 174 deletions(-) delete mode 100644 glyph.cpp delete mode 100644 glyph.h diff --git a/forceScheme.cpp b/forceScheme.cpp index 3683c10..de856ba 100644 --- a/forceScheme.cpp +++ b/forceScheme.cpp @@ -11,15 +11,14 @@ arma::mat mp::forceScheme(const arma::mat &D, double tol, double fraction) { - - arma::uword n = (arma::uword) Y.n_rows; + arma::uword n = Y.n_rows; V i(n), j(n); for (arma::uword k = 0; k < n; k++) i[k] = j[k] = k; - double prev_delta_sum = 1. / 0.; + double prevDeltaSum = 1. / 0.; for (size_t iter = 0; iter < maxIter; iter++) { - double delta_sum = 0; + double deltaSum = 0; arma::shuffle(i); for (V::iterator a = i.begin(); a != i.end(); a++) { @@ -31,14 +30,14 @@ arma::mat mp::forceScheme(const arma::mat &D, arma::rowvec direction(Y.row(*b) - Y.row(*a)); double d2 = std::max(arma::norm(direction, 2), mp::EPSILON); double delta = (D(*a, *b) - d2) / fraction; - delta_sum += fabs(delta); + deltaSum += fabs(delta); Y.row(*b) += delta * (direction / d2); } } - if (fabs(prev_delta_sum - delta_sum) < tol) + if (fabs(prevDeltaSum - deltaSum) < tol) break; - prev_delta_sum = delta_sum; + prevDeltaSum = deltaSum; } return Y; diff --git a/glyph.cpp b/glyph.cpp deleted file mode 100644 index f143ee8..0000000 --- a/glyph.cpp +++ /dev/null @@ -1,103 +0,0 @@ -#include "glyph.h" - -#include -#include -#include -#include -#include - -const float PI = 3.1415f; - -Glyph::Glyph(QQuickItem *parent) - : QQuickItem(parent) - , m_size(0) -{ - setFlag(QQuickItem::ItemHasContents); -} - -void Glyph::setSize(qreal size) -{ - if (size == m_size) - return; - - m_size = size; - setWidth(size); - setHeight(size); - emit sizeChanged(); - update(); -} - -void Glyph::setColor(const QColor &color) -{ - if (color == m_color) - return; - - m_color = color; - emit colorChanged(); - update(); -} - -int calculateCircleVertexCount(qreal radius) -{ - // 10 * sqrt(r) \approx 2*pi / acos(1 - 1 / (4*r)) - return (int) (10.0 * sqrt(radius)); -} - -void updateCircleGeometry(QSGGeometry *geometry, const QRectF &bounds) -{ - int vertexCount = geometry->vertexCount(); - float cy = bounds.center().x(); - float cx = bounds.center().y(); - - float theta = 2 * PI / float(vertexCount); - float c = cosf(theta); - float s = sinf(theta); - float x = bounds.width() / 2; - float y = 0; - - QSGGeometry::Point2D *vertexData = geometry->vertexDataAsPoint2D(); - for (int i = 0; i < vertexCount; i++) { - vertexData[i].set(x + cx, y + cy); - - float t = x; - x = c*x - s*y; - y = s*t + c*y; - } -} - -QSGNode *Glyph::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) -{ - QSGGeometryNode *node = 0; - QSGGeometry *geometry = 0; - QSGFlatColorMaterial *material = 0; - int vertexCount = calculateCircleVertexCount(m_size / 2); - - if (!oldNode) { - node = new QSGGeometryNode; - - geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), vertexCount); - geometry->setDrawingMode(GL_POLYGON); - node->setGeometry(geometry); - node->setFlag(QSGNode::OwnsGeometry); - - material = new QSGFlatColorMaterial; - material->setColor(m_color); - node->setMaterial(material); - node->setFlag(QSGNode::OwnsMaterial); - } else { - node = static_cast(oldNode); - geometry = node->geometry(); - geometry->allocate(vertexCount); - } - - updateCircleGeometry(geometry, boundingRect()); - node->markDirty(QSGNode::DirtyGeometry); - - material = static_cast(node->material()); - if (material->color() != m_color) { - material->setColor(m_color); - node->markDirty(QSGNode::DirtyMaterial); - } - - return node; -} diff --git a/glyph.h b/glyph.h deleted file mode 100644 index 7532d04..0000000 --- a/glyph.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef GLYPH_H -#define GLYPH_H - -#include -#include - -class Glyph : public QQuickItem -{ - Q_OBJECT - Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) - Q_PROPERTY(qreal size READ size WRITE setSize NOTIFY sizeChanged) - -public: - enum GlyphType { - GLYPH_CIRCLE, - GLYPH_SQUARE, - GLYPH_STAR, - GLYPH_CROSS - }; - - Glyph(QQuickItem *parent = 0); - - QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *); - - QColor color() const { return m_color; } - void setColor(const QColor &color); - - qreal size() const { return m_size; } - void setSize(qreal size); - -signals: - void colorChanged(); - void sizeChanged(); - -public slots: - -private: - QColor m_color; - qreal m_size; -}; - -#endif // GLYPH_H diff --git a/main.cpp b/main.cpp index 863a20d..794cbc1 100644 --- a/main.cpp +++ b/main.cpp @@ -25,7 +25,6 @@ int main(int argc, char **argv) QGuiApplication app(argc, argv); qmlRegisterType("PM", 1, 0, "Scatterplot"); - qmlRegisterType("PM", 1, 0, "Glyph"); QQuickView view; QSurfaceFormat format = view.format(); diff --git a/mp.h b/mp.h index b3f9c75..6508d9d 100644 --- a/mp.h +++ b/mp.h @@ -6,6 +6,6 @@ static const double EPSILON = 1e-3; double euclidean(const arma::rowvec &x1, const arma::rowvec &x2); arma::mat dist(const arma::mat &X, double (*distCalc)(const arma::rowvec &, const arma::rowvec &) = euclidean); arma::mat lamp(const arma::mat &X, const arma::uvec &sampleIndices, const arma::mat &Ys); -arma::mat forceScheme(const arma::mat &D, arma::mat &Y, size_t maxIter = 20, double tol = 1e-3, double fraction = 0.25); +arma::mat forceScheme(const arma::mat &D, arma::mat &Y, size_t maxIter = 20, double tol = 1e-3, double fraction = 8); } // namespace mp diff --git a/pm.pro b/pm.pro index a8d87d7..2ba534d 100644 --- a/pm.pro +++ b/pm.pro @@ -1,11 +1,9 @@ QT += qml quick QMAKE_LIBS += -larmadillo -HEADERS += glyph.h \ - scatterplot.h \ +HEADERS += scatterplot.h \ mp.h SOURCES += main.cpp \ - glyph.cpp \ scatterplot.cpp \ lamp.cpp \ forceScheme.cpp \ diff --git a/scatterplot.cpp b/scatterplot.cpp index e775661..b5e8261 100644 --- a/scatterplot.cpp +++ b/scatterplot.cpp @@ -1,11 +1,16 @@ #include "scatterplot.h" +#include #include -#include "glyph.h" #include +#include #include +#include +#include -const int GLYPH_SIZE = 10; +const int GLYPH_SIZE = 4; +const float PADDING = 5; +const float PI = 3.1415f; Scatterplot::Scatterplot() { @@ -22,38 +27,93 @@ void Scatterplot::setData(const arma::mat &data) return; m_data = data; - qreal xmin = m_data.col(0).min(), - xmax = m_data.col(0).max(), - ymin = m_data.col(1).min(), - ymax = m_data.col(1).max(); + update(); +} - for (arma::uword i = 0; i < m_data.n_rows; i++) { - arma::rowvec row = m_data.row(i); +int calculateCircleVertexCount(qreal radius) +{ + // 10 * sqrt(r) \approx 2*pi / acos(1 - 1 / (4*r)) + return (int) (10.0 * sqrt(radius)); +} + +void updateCircleGeometry(QSGGeometry *geometry, float size, float cx, float cy) +{ + int vertexCount = geometry->vertexCount(); - Glyph *glyph = new Glyph(); + float theta = 2 * PI / float(vertexCount); + float c = cosf(theta); + float s = sinf(theta); + float x = size / 2; + float y = 0; - glyph->setSize(5); - glyph->setX((row[0] - xmin) / (xmax - xmin) * width()); - glyph->setY((row[1] - ymin) / (ymax - ymin) * height()); + QSGGeometry::Point2D *vertexData = geometry->vertexDataAsPoint2D(); + for (int i = 0; i < vertexCount; i++) { + vertexData[i].set(x + cx, y + cy); - glyph->setParent(this); + float t = x; + x = c*x - s*y; + y = s*t + c*y; } +} - update(); +void updateSquareGeometry(QSGGeometry *geometry, float size, float cx, float cy) +{ + float r = size / 2; + QSGGeometry::Point2D *vertexData = geometry->vertexDataAsPoint2D(); + vertexData[0].set(cx - r, cy - r); + vertexData[1].set(cx + r, cy - r); + vertexData[2].set(cx + r, cy + r); + vertexData[3].set(cx - r, cy + r); } QSGNode *Scatterplot::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) { QSGNode *node = 0; + QSGGeometryNode *childNode = 0; + QSGGeometry *geometry = 0; + QSGFlatColorMaterial *material = 0; + int vertexCount = calculateCircleVertexCount(GLYPH_SIZE / 2); + qreal xmin = m_data.col(0).min(), + xmax = m_data.col(0).max(), + ymin = m_data.col(1).min(), + ymax = m_data.col(1).max(), + x, y; if (!oldNode) { node = new QSGNode; - for (QObjectList::const_iterator it = children().begin(); it != children().end(); it++) - node->appendChildNode(static_cast(*it)->updatePaintNode(0, 0)); + for (arma::uword i = 0; i < m_data.n_rows; i++) { + arma::rowvec row = m_data.row(i); + x = (row[0] - xmin) / (xmax - xmin) * width(); + y = (row[1] - ymin) / (ymax - ymin) * height(); + + childNode = new QSGGeometryNode; + + geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), vertexCount); + geometry->setDrawingMode(GL_POLYGON); + childNode->setGeometry(geometry); + childNode->setFlag(QSGNode::OwnsGeometry); + + material = new QSGFlatColorMaterial; + material->setColor(QColor()); + childNode->setMaterial(material); + childNode->setFlag(QSGNode::OwnsMaterial); + + node->appendChildNode(childNode); + } } else { - node = static_cast(oldNode); + node = oldNode; } + childNode = static_cast(node->firstChild()); + for (arma::uword i = 0; i < m_data.n_rows; i++) { + arma::rowvec row = m_data.row(i); + x = (row[0] - xmin) / (xmax - xmin) * width(); + y = (row[1] - ymin) / (ymax - ymin) * height(); + + geometry = childNode->geometry(); + updateCircleGeometry(geometry, GLYPH_SIZE, x, y); + childNode = static_cast(childNode->nextSibling()); + } node->markDirty(QSGNode::DirtyGeometry); return node; -- cgit v1.2.3