From 869ada0bca37aafc9a803f3226458f163db5f3c8 Mon Sep 17 00:00:00 2001 From: Stanislaw Halik Date: Wed, 26 Oct 2016 06:54:58 +0200 Subject: spline-widget: fix logic for detecting points being too close It was still possible to reorder points by dragging one of them. Also get rid of the iteration. It was previously introduced to try converge one of the points to another position. Allow for changing Y value while the point is dragged. Take snap into account. Use newly introduced "iround". --- spline-widget/spline-widget.cpp | 113 ++++++++++++++++++---------------------- spline-widget/spline-widget.hpp | 4 +- 2 files changed, 53 insertions(+), 64 deletions(-) (limited to 'spline-widget') diff --git a/spline-widget/spline-widget.cpp b/spline-widget/spline-widget.cpp index 21c52d63..105efb77 100644 --- a/spline-widget/spline-widget.cpp +++ b/spline-widget/spline-widget.cpp @@ -19,6 +19,8 @@ #include #include +#include + #include #include @@ -139,7 +141,7 @@ void spline_widget::drawBackground() // vertical grid for (int i = 0; i <= maxx; i += ystep) { - const int x = int(std::round(pixel_bounds.x() + i * c.x())); + const int x = iround(pixel_bounds.x() + i * c.x()); drawLine(painter, QPoint(x, pixel_bounds.y()), QPoint(x, pixel_bounds.y() + pixel_bounds.height()), @@ -166,11 +168,11 @@ void spline_widget::drawFunction() { const QPen pen(Qt::white, 1, Qt::SolidLine, Qt::FlatCap); const QPointF prev_ = point_to_pixel(QPointF(0, 0)); - QPoint prev(int(std::round(prev_.x())), int(std::round(prev_.y()))); + QPoint prev(iround(prev_.x()), iround(prev_.y())); for (int i = 0; i < points.size(); i++) { const QPointF tmp_ = point_to_pixel(points[i]); - const QPoint tmp(int(std::round(tmp_.x())), int(std::round(tmp_.y()))); + const QPoint tmp(iround(tmp_.x()), iround(tmp_.y())); drawLine(painter, prev, tmp, pen); prev = tmp; } @@ -286,6 +288,8 @@ void spline_widget::mousePressEvent(QMouseEvent *e) if (!is_in_bounds(e->pos())) return; + const int point_pixel_closeness_limit = get_closeness_limit(); + points_t points = _config->getPoints(); if (e->button() == Qt::LeftButton) { @@ -305,13 +309,13 @@ void spline_widget::mousePressEvent(QMouseEvent *e) if (!bTouchingPoint) { bool too_close = false; - const auto pos = e->pos(); + const QPoint pos = e->pos(); for (int i = 0; i < points.size(); i++) { const QPointF pt = point_to_pixel(points[i]); const qreal x = std::fabs(pt.x() - pos.x()); - if (point_closeness_limit >= x) + if (point_pixel_closeness_limit >= x) { too_close = true; break; @@ -360,62 +364,45 @@ void spline_widget::mouseMoveEvent(QMouseEvent *e) return; } - points_t points = _config->getPoints(); + const int i = moving_control_point_idx; + const points_t points = _config->getPoints(); + const int sz = points.size(); - if (moving_control_point_idx >= 0 && moving_control_point_idx < points.size()) + if (i >= 0 && i < sz) { - const int idx = moving_control_point_idx; - - setCursor(Qt::ClosedHandCursor); - + const int point_pixel_closeness_limit = get_closeness_limit(); bool overlap = false; - QPoint pix = e->pos(); + const QPoint pix = progn( + // takes snap into account + const QPointF pix_ = point_to_pixel(pixel_coord_to_point(e->pos())); + return QPoint(int(pix_.x()), int(pix_.y())); + ); + QPointF new_pt = pixel_coord_to_point(pix); - for (int i = 0; i < 2; i++) + if (i + 1 < points.size()) { - bool bad = false; - if (idx + 1 < points.size()) - { - auto other = points[idx+1]; - auto other_pix = point_to_pixel(other); - bad = pix.x() + point_closeness_limit > other_pix.x(); - if (i == 0 && bad) - { - pix.setX(int(std::round(other_pix.x() - point_closeness_limit))); - new_pt = pixel_coord_to_point(pix); - } - else - overlap |= bad; - } - if (idx != 0) - { - const QPointF other = points[idx-1]; - const QPointF other_pix = point_to_pixel(other); - bad = pix.x() - point_closeness_limit < other_pix.x(); - if (i == 0 && bad) - { - pix.setX(int(std::round(other_pix.x() + point_closeness_limit))); - new_pt = pixel_coord_to_point(pix); - } - else - overlap |= bad; - } - if (!bad) - break; + const QPointF other_pix = point_to_pixel(points[i+1]); + overlap |= other_pix.x() - pix.x() < point_pixel_closeness_limit; } - - if (!overlap) + if (i != 0) { - _config->movePoint(idx, new_pt); - _draw_function = true; - - show_tooltip(pix, new_pt); - update(); + const QPointF other_pix = point_to_pixel(points[i-1]); + overlap |= pix.x() - other_pix.x() < point_pixel_closeness_limit; } + + if (overlap) + new_pt.setX(points[i].x()); + + _config->movePoint(i, new_pt); + _draw_function = true; + + setCursor(Qt::ClosedHandCursor); + show_tooltip(pix, new_pt); + update(); } - else + else if (sz) { int i; bool is_on_point = is_on_pt(e->pos(), &i); @@ -446,8 +433,6 @@ void spline_widget::mouseReleaseEvent(QMouseEvent *e) if (e->button() == Qt::LeftButton) { - //mouseMoveEvent(e); - { if (is_on_pt(e->pos(), nullptr)) setCursor(Qt::CrossCursor); @@ -472,12 +457,17 @@ void spline_widget::reload_spline() update_range(); } +int spline_widget::get_closeness_limit() +{ + return std::max(iround(snap_x * c.x()), 1); +} + void spline_widget::show_tooltip(const QPoint& pos, const QPointF& value_, const QString& prefix) { const QPointF value = QPoint(0, 0) == value_ ? pixel_coord_to_point(pos) : value_; double x = value.x(), y = value.y(); - const int x_ = int(std::round(x)), y_ = int(std::round(y)); + const int x_ = iround(x), y_ = iround(y); using std::fabs; @@ -500,11 +490,12 @@ void spline_widget::show_tooltip(const QPoint& pos, const QPointF& value_, const bool spline_widget::is_in_bounds(const QPoint& pos) const { - static constexpr int grace = 2; - return !(pos.x() + grace < pixel_bounds.left() || - pos.x() - grace > pixel_bounds.right() || - pos.y() + grace < pixel_bounds.top() || - pos.y() - grace > pixel_bounds.bottom()); + static constexpr int grace = point_size * 3; + static constexpr int bottom_grace = int(point_size * 1.5); + return (pos.x() + grace > pixel_bounds.left() && + pos.x() - grace < pixel_bounds.right() && + pos.y() + grace > pixel_bounds.top() && + pos.y() - bottom_grace < pixel_bounds.bottom()); } void spline_widget::update_range() @@ -545,8 +536,6 @@ void spline_widget::focusOutEvent(QFocusEvent* e) QPointF spline_widget::pixel_coord_to_point(const QPoint& point) { - using std::round; - qreal x = (point.x() - pixel_bounds.x()) / c.x(); qreal y = (pixel_bounds.height() - point.y() + pixel_bounds.y()) / c.y(); @@ -580,8 +569,8 @@ QPointF spline_widget::pixel_coord_to_point(const QPoint& point) QPointF spline_widget::point_to_pixel(const QPointF& point) { - return QPoint(int(std::round(pixel_bounds.x() + point.x() * c.x())), - int(std::round(pixel_bounds.y() + pixel_bounds.height() - point.y() * c.y()))); + return QPoint(iround(pixel_bounds.x() + point.x() * c.x()), + iround(pixel_bounds.y() + pixel_bounds.height() - point.y() * c.y())); } void spline_widget::resizeEvent(QResizeEvent *) diff --git a/spline-widget/spline-widget.hpp b/spline-widget/spline-widget.hpp index d94d2e8e..8886016c 100644 --- a/spline-widget/spline-widget.hpp +++ b/spline-widget/spline-widget.hpp @@ -56,6 +56,7 @@ protected slots: void mouseReleaseEvent(QMouseEvent *e) override; void reload_spline(); private: + int get_closeness_limit(); void show_tooltip(const QPoint& pos, const QPointF& value = QPointF(0, 0), const QString& prefix = QStringLiteral("")); bool is_in_bounds(const QPoint& pos) const; @@ -67,7 +68,7 @@ private: void focusOutEvent(QFocusEvent*e) override; void resizeEvent(QResizeEvent *) override; -private: + bool is_on_pt(const QPoint& pos, int* pt = nullptr); void update_range(); QPointF pixel_coord_to_point(const QPoint& point); @@ -91,5 +92,4 @@ private: static constexpr int line_length_pixels = 3; static constexpr int point_size = 4; - static constexpr int point_closeness_limit = 7; }; -- cgit v1.2.3