summaryrefslogtreecommitdiffhomepage
path: root/spline
diff options
context:
space:
mode:
authorStanislaw Halik <sthalik@misaki.pl>2019-01-16 06:11:48 +0100
committerStanislaw Halik <sthalik@misaki.pl>2019-01-16 07:49:13 +0100
commit07b45ca4578ccaed91f7f3c70e82dc7ffbdf47ab (patch)
tree0904b728158414937919f62714358725f52e7400 /spline
parent1e04979c3452d4eac633677876a88f9411a1153d (diff)
spline: fix deadlock, logic error
Tracking rarely deadlocked when saving mappings. Investigating it further also shown how a wrong bundle was used for Accela's splines.
Diffstat (limited to 'spline')
-rw-r--r--spline/axis-opts.cpp6
-rw-r--r--spline/axis-opts.hpp17
-rw-r--r--spline/spline-widget.cpp83
-rw-r--r--spline/spline-widget.hpp8
-rw-r--r--spline/spline.cpp295
-rw-r--r--spline/spline.hpp57
6 files changed, 243 insertions, 223 deletions
diff --git a/spline/axis-opts.cpp b/spline/axis-opts.cpp
index fa527877..489008e6 100644
--- a/spline/axis-opts.cpp
+++ b/spline/axis-opts.cpp
@@ -27,14 +27,14 @@ static max_clamp get_max_y(Axis k)
}
axis_opts::axis_opts(QString pfx, Axis idx) :
+ prefix_(pfx),
+ axis_(idx),
zero(b_settings_window, n(pfx, "zero-pos"), 0),
src(b_settings_window, n(pfx, "source-index"), idx),
invert(b_settings_window, n(pfx, "invert-sign"), false),
altp(b_mapping_window, n(pfx, "alt-axis-sign"), false),
clamp_x_(b_mapping_window, n(pfx, "max-value"), get_max_x(idx)),
- clamp_y_(b_mapping_window, n(pfx, "max-output-value"), get_max_y(idx)),
- prefix_(pfx),
- axis_(idx)
+ clamp_y_(b_mapping_window, n(pfx, "max-output-value"), get_max_y(idx))
{}
QString const& axis_opts::prefix() const { return prefix_; }
diff --git a/spline/axis-opts.hpp b/spline/axis-opts.hpp
index b4475502..c773dd61 100644
--- a/spline/axis-opts.hpp
+++ b/spline/axis-opts.hpp
@@ -9,8 +9,14 @@ namespace axis_opts_impl {
using namespace options;
-struct OTR_SPLINE_EXPORT axis_opts final
+class OTR_SPLINE_EXPORT axis_opts final
{
+ QString prefix_;
+ Axis axis_;
+
+ static inline QString n(QString const& pfx, QString const& name);
+
+public:
enum max_clamp
{
r180 = 180,
@@ -37,8 +43,8 @@ struct OTR_SPLINE_EXPORT axis_opts final
};
// note, these two bundles can be the same value with no issues
- bundle b_settings_window = make_bundle("opentrack-ui");
- bundle b_mapping_window = make_bundle("opentrack-mappings");
+ bundle b_settings_window{ make_bundle(axis_ == Axis(-1) ? QString() : "opentrack-ui") };
+ bundle b_mapping_window{ make_bundle(axis_ == Axis(-1) ? QString() : "opentrack-mappings") };
value<double> zero;
value<int> src;
value<bool> invert, altp;
@@ -49,11 +55,6 @@ struct OTR_SPLINE_EXPORT axis_opts final
QString const& prefix() const;
Axis axis() const;
-private:
- static inline QString n(QString const& pfx, QString const& name);
-
- QString prefix_;
- Axis axis_;
};
} // ns axis_opts_impl
diff --git a/spline/spline-widget.cpp b/spline/spline-widget.cpp
index bfe8aff1..d329d8e2 100644
--- a/spline/spline-widget.cpp
+++ b/spline/spline-widget.cpp
@@ -1,10 +1,3 @@
-/* Copyright (c) 2012-2016 Stanislaw Halik <sthalik@misaki.pl>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- */
-
#include "spline-widget.hpp"
#include "compat/math.hpp"
#include "compat/macros.hpp"
@@ -19,7 +12,7 @@
#include <QDebug>
-using namespace spline_detail;
+namespace spline_detail {
spline_widget::spline_widget(QWidget *parent) : QWidget(parent)
{
@@ -37,7 +30,7 @@ spline_widget::~spline_widget()
}
}
-void spline_widget::setConfig(base_spline* spl)
+void spline_widget::set_config(base_spline* spl)
{
if (connection)
{
@@ -49,13 +42,13 @@ void spline_widget::setConfig(base_spline* spl)
if (spl)
{
- update_range();
-
- std::shared_ptr<base_spline::base_settings> s = spl->get_settings();
- connection = connect(s.get(), &base_spline::base_settings::recomputed,
+ std::shared_ptr<base_settings> s = spl->get_settings();
+ connection = connect(s.get(), &base_settings::recomputed,
this, [this] { reload_spline(); },
Qt::QueuedConnection);
}
+
+ reload_spline();
}
QColor spline_widget::colorBezier() const
@@ -71,7 +64,7 @@ void spline_widget::setColorBezier(QColor const& color)
void spline_widget::force_redraw()
{
- background_img = QPixmap();
+ background_img = {};
repaint();
}
@@ -146,7 +139,6 @@ void spline_widget::drawBackground()
#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
metrics.horizontalAdvance(text);
#else
- // XXX remove after Linux upgrades -sh 20181225
metrics.width(text);
#endif
@@ -162,7 +154,7 @@ void spline_widget::drawFunction()
QPainter painter(&spline_img);
painter.setRenderHint(QPainter::Antialiasing, true);
- const points_t points = config->get_points();
+ const points_t& points = config->get_points();
if (moving_control_point_idx >= 0 &&
moving_control_point_idx < points.size())
@@ -170,7 +162,7 @@ void spline_widget::drawFunction()
const QPen pen(Qt::white, 1, Qt::SolidLine, Qt::FlatCap);
const QPointF prev_ = point_to_pixel({});
QPointF prev(iround(prev_.x()), iround(prev_.y()));
- for (auto point : points)
+ for (const auto& point : points)
{
const QPointF tmp = point_to_pixel(point);
drawLine(painter, prev, tmp, pen);
@@ -219,9 +211,9 @@ void spline_widget::drawFunction()
for (double k = 0; k < maxx; k += step*3) // NOLINT
{
- const auto next_1 = (double) config->get_value_no_save(k + step*1);
- const auto next_2 = (double) config->get_value_no_save(k + step*2);
- const auto next_3 = (double) config->get_value_no_save(k + step*3);
+ const auto next_1 = config->get_value_no_save(k + step*1);
+ const auto next_2 = config->get_value_no_save(k + step*2);
+ const auto next_3 = config->get_value_no_save(k + step*3);
QPointF b(clamp(point_to_pixel({k + step*1, next_1}))),
c(clamp(point_to_pixel({k + step*2, next_2}))),
@@ -235,14 +227,14 @@ void spline_widget::drawFunction()
QPointF prev = point_to_pixel({});
for (double i = 0; i < maxx; i += step) // NOLINT
{
- const auto val = (double) config->get_value_no_save(i);
+ const auto val = config->get_value_no_save(i);
const QPointF cur = point_to_pixel({i, val});
painter.drawLine(prev, cur);
prev = cur;
}
{
const double maxx = config->max_input();
- const double maxy = double(config->get_value_no_save(maxx));
+ const double maxy = config->get_value_no_save(maxx);
painter.drawLine(prev, point_to_pixel({ maxx, maxy }));
}
#endif
@@ -281,10 +273,12 @@ void spline_widget::paintEvent(QPaintEvent *e)
if (background_img.size() != QSize(W, H))
{
- background_img = QPixmap(W, H);
+ update_range();
+
+ background_img = { W, H };
background_img.setDevicePixelRatio(dpr);
- draw_function = true;
drawBackground();
+ draw_function = true;
}
if (draw_function)
@@ -333,7 +327,7 @@ void spline_widget::mousePressEvent(QMouseEvent *e)
moving_control_point_idx = -1;
- points_t points = config->get_points();
+ const points_t& points = config->get_points();
if (e->button() == Qt::LeftButton)
{
@@ -409,7 +403,7 @@ void spline_widget::mouseMoveEvent(QMouseEvent *e)
}
const int i = moving_control_point_idx;
- const points_t points = config->get_points();
+ const points_t& points = config->get_points();
const int sz = points.size();
if (i >= 0 && i < sz)
@@ -501,7 +495,7 @@ void spline_widget::mouseReleaseEvent(QMouseEvent *e)
void spline_widget::reload_spline()
{
// don't recompute here as the value's about to be recomputed in the callee
- update_range();
+ background_img = {};
update();
}
@@ -519,7 +513,7 @@ void spline_widget::show_tooltip(const QPoint& pos, const QPointF& value_)
double x = value.x(), y = value.y();
if (preview_only)
- y = (double)config->get_value_no_save(x);
+ y = config->get_value_no_save(x);
const int x_ = iround(x), y_ = iround(y);
@@ -563,14 +557,15 @@ void spline_widget::update_range()
const int mwl = 40, mhl = 20;
const int mwr = 15, mhr = 35;
- pixel_bounds = QRect(mwl, mhl, (w - mwl - mwr), (h - mhl - mhr));
- c = { pixel_bounds.width() / config->max_input(), pixel_bounds.height() / config->max_output() };
- draw_function = true;
-
- background_img = QPixmap();
- spline_img = QPixmap();
+ pixel_bounds = { mwl, mhl, (w - mwl - mwr), (h - mhl - mhr) };
+ c = {
+ pixel_bounds.width() / std::fmax(1, config->max_input()),
+ pixel_bounds.height() / std::fmax(1, config->max_output())
+ };
- repaint();
+ draw_function = true;
+ background_img = {};
+ spline_img = {};
}
bool spline_widget::point_within_pixel(const QPointF& pt, const QPointF& pixel)
@@ -631,7 +626,7 @@ QPointF spline_widget::point_to_pixel(const QPointF& point)
void spline_widget::resizeEvent(QResizeEvent *)
{
- update_range();
+ reload_spline();
}
bool spline_widget::is_on_pt(const QPointF& pos, int* pt)
@@ -643,7 +638,7 @@ bool spline_widget::is_on_pt(const QPointF& pos, int* pt)
return false;
}
- const points_t points = config->get_points();
+ const points_t& points = config->get_points();
for (int i = 0; i < points.size(); i++)
{
@@ -659,3 +654,17 @@ bool spline_widget::is_on_pt(const QPointF& pos, int* pt)
*pt = -1;
return false;
}
+
+void spline_widget::changeEvent(QEvent* e)
+{
+ switch (e->type())
+ {
+ case QEvent::EnabledChange:
+ background_img = {}; spline_img = {};
+ update();
+ break;
+ default:
+ break;
+ }
+}
+} // ns spline_detail
diff --git a/spline/spline-widget.hpp b/spline/spline-widget.hpp
index a8adfce1..b0a76e2d 100644
--- a/spline/spline-widget.hpp
+++ b/spline/spline-widget.hpp
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016 Stanislaw Halik <sthalik@misaki.pl>
+/* Copyright (c) 2012-2019 Stanislaw Halik <sthalik@misaki.pl>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -34,12 +34,11 @@ class OTR_SPLINE_EXPORT spline_widget final : public QWidget
Q_PROPERTY(int x_step READ x_step WRITE set_x_step)
Q_PROPERTY(int y_step READ y_step WRITE set_y_step)
- using points_t = base_spline::points_t;
public:
explicit spline_widget(QWidget *parent = nullptr);
~spline_widget() override;
- void setConfig(base_spline* spl);
+ void set_config(base_spline* spl);
QColor colorBezier() const;
void setColorBezier(QColor const& color);
@@ -78,6 +77,7 @@ private:
bool is_on_pt(const QPointF& pos, int* pt = nullptr);
void update_range();
+ void changeEvent(QEvent* e) override;
QPointF pixel_to_point(const QPointF& point);
QPointF point_to_pixel(const QPointF& point);
@@ -103,7 +103,7 @@ private:
bool draw_function = true, preview_only = false;
// point's circle radius on the widget
- static constexpr inline int point_size_in_pixels_ = 4;
+ static constexpr int point_size_in_pixels_ = 4;
const double point_size_in_pixels = point_size_in_pixels_ * std::fmax(1, devicePixelRatioF() * .66);
};
diff --git a/spline/spline.cpp b/spline/spline.cpp
index 50812bad..3ec3b5dc 100644
--- a/spline/spline.cpp
+++ b/spline/spline.cpp
@@ -1,11 +1,3 @@
-/* Copyright (c) 2012-2016, Stanislaw Halik <sthalik@misaki.pl>
-
- * Permission to use, copy, modify, and/or distribute this
- * software for any purpose with or without fee is hereby granted,
- * provided that the above copyright notice and this permission
- * notice appear in all copies.
- */
-
#include "spline.hpp"
#include "compat/math.hpp"
@@ -18,7 +10,6 @@
#include <QObject>
#include <QMutexLocker>
-#include <QCoreApplication>
#include <QPointF>
#include <QSettings>
#include <QString>
@@ -27,6 +18,7 @@
namespace spline_detail {
+settings::~settings() = default;
base_spline_::~base_spline_() = default;
base_spline::~base_spline() = default;
spline_modify_mixin::~spline_modify_mixin() = default;
@@ -40,7 +32,6 @@ spline::spline(const QString& name, const QString& axis_name, Axis axis)
spline::~spline()
{
QMutexLocker l(&mtx);
-
disconnect_signals();
}
@@ -48,46 +39,53 @@ spline::spline() : spline(QString{}, QString{}, Axis(-1)) {}
void spline::set_tracking_active(bool value)
{
- QMutexLocker l(&mtx);
- activep = value;
+ std::shared_ptr<settings> S;
+ {
+ QMutexLocker l(&mtx);
+ S = s;
+ activep = value;
+ }
+ emit S->recomputed();
}
bundle spline::get_bundle()
{
- QMutexLocker l(&mtx); // avoid logic errors due to changes in value<t> data
+ QMutexLocker l(&mtx);
return s->b;
}
void spline::clear()
{
+ std::shared_ptr<settings> S;
{
QMutexLocker l(&mtx);
+ S = s;
s->points = {};
+ points = {};
+ update_interp_data();
}
-
- invalidate_settings();
+ emit S->recomputed();
}
-float spline::get_value(double x)
+double spline::get_value(double x) const
{
- QMutexLocker foo(&mtx);
+ QMutexLocker l(&mtx);
- const float ret = get_value_no_save(x);
- last_input_value.setX(std::fabs(x));
- last_input_value.setY(double(std::fabs(ret)));
+ const double ret = get_value_no_save(x);
+ last_input_value = { std::fabs(x), std::fabs((double)ret) };
return ret;
}
-float spline::get_value_no_save(double x) const
+double spline::get_value_no_save(double x) const
{
- QMutexLocker foo(&mtx);
+ QMutexLocker l(&mtx);
- float q = float(x * bucket_size_coefficient(points));
+ double q = x * bucket_size_coefficient(points);
int xi = (int)q;
- float yi = get_value_internal(xi);
- float yiplus1 = get_value_internal(xi+1);
- float f = (q-xi);
- float ret = yiplus1 * f + yi * (1.0f - f); // at least do a linear interpolation.
+ double yi = get_value_internal(xi);
+ double yiplus1 = get_value_internal(xi+1);
+ double f = (q-xi);
+ double ret = yiplus1 * f + yi * (1 - f); // at least do a linear interpolation.
return ret;
}
@@ -95,43 +93,47 @@ bool spline::get_last_value(QPointF& point)
{
QMutexLocker foo(&mtx);
point = last_input_value;
- return activep;
+ return activep && point.y() >= 0;
}
-float spline::get_value_internal(int x) const
+double spline::get_value_internal(int x) const
{
- if (!validp)
- {
- update_interp_data();
- validp = true;
- }
-
const float sign = signum(x);
x = std::abs(x);
- const float ret_ = data[std::min(unsigned(x), unsigned(value_count)-1u)];
+ const float ret_ = data[std::min(unsigned(x), value_count - 1)];
return sign * clamp(ret_, 0, 1000);
}
-QPointF spline::ensure_in_bounds(const QList<QPointF>& points, int i)
+void spline::ensure_in_bounds(const QList<QPointF>& points, int i, f& x, f& y)
{
const int sz = points.size();
if (i < 0 || sz == 0)
- return {};
-
- if (i < sz)
- return points[i];
-
- return points[sz - 1];
+ {
+ x = 0;
+ y = 0;
+ }
+ else if (i < sz)
+ {
+ const QPointF& pt = points[i];
+ x = (f)pt.x();
+ y = (f)pt.y();
+ }
+ else
+ {
+ const QPointF& pt = points[sz - 1];
+ x = (f)pt.x();
+ y = (f)pt.y();
+ }
}
int spline::element_count(const QList<QPointF>& points, double max_input)
{
const unsigned sz = (unsigned)points.size();
- for (unsigned k = 0; k < sz; k++)
+ for (int k = sz-1; k >= 0; k--)
{
const QPointF& pt = points[k];
- if (max_input > 1e-4 && pt.x() - 1e-2 > max_input)
+ if (max_input > 1e-4 && pt.x() - 1e-4 >= max_input)
return k;
}
return sz;
@@ -148,15 +150,11 @@ void spline::update_interp_data() const
ensure_valid(list);
const int sz = list.size();
- const double maxx = max_input();
-
if (list.isEmpty())
- list.prepend({ maxx, max_output() });
+ list.prepend({ max_input(), max_output() });
const double c = bucket_size_coefficient(list);
- const double c_interp = c * 30;
-
- enum { magic_fill_value = -255 };
+ const double c_ = c * c_interp;
for (unsigned i = 0; i < value_count; i++)
data[i] = magic_fill_value;
@@ -166,35 +164,35 @@ void spline::update_interp_data() const
const QPointF& pt = list[0];
const double x = pt.x();
const double y = pt.y();
- const unsigned max = clamp(uround(x * c), 0, value_count-2);
+ const unsigned max = clamp(uround(x * c), 0, value_count-1);
for (unsigned k = 0; k <= max; k++)
data[k] = float(y * k / max); // no need for bresenham
}
else
{
- if (list[0].x() > 1e-2 && list[0].x() <= maxx)
- list.push_front(QPointF(0, 0));
+ if (list[0].x() > 1e-2)
+ list.push_front({});
// now this is hella expensive due to `c_interp'
for (int i = 0; i < sz; i++)
{
- const QPointF p0 = ensure_in_bounds(list, i - 1);
- const QPointF p1 = ensure_in_bounds(list, i + 0);
- const QPointF p2 = ensure_in_bounds(list, i + 1);
- const QPointF p3 = ensure_in_bounds(list, i + 2);
- const double p0_x = p0.x(), p1_x = p1.x(), p2_x = p2.x(), p3_x = p3.x();
- const double p0_y = p0.y(), p1_y = p1.y(), p2_y = p2.y(), p3_y = p3.y();
-
- const double cx[4] = {
+ f p0_x, p1_x, p2_x, p3_x;
+ f p0_y, p1_y, p2_y, p3_y;
+
+ ensure_in_bounds(list, i - 1, p0_x, p0_y);
+ ensure_in_bounds(list, i + 0, p1_x, p1_y);
+ ensure_in_bounds(list, i + 1, p2_x, p2_y);
+ ensure_in_bounds(list, i + 2, p3_x, p3_y);
+
+ const f cx[4] = {
2 * p1_x, // 1
-p0_x + p2_x, // t
2 * p0_x - 5 * p1_x + 4 * p2_x - p3_x, // t^2
-p0_x + 3 * p1_x - 3 * p2_x + p3_x, // t3
};
- const double cy[4] =
- {
+ const f cy[4] = {
2 * p1_y, // 1
-p0_y + p2_y, // t
2 * p0_y - 5 * p1_y + 4 * p2_y - p3_y, // t^2
@@ -202,36 +200,37 @@ void spline::update_interp_data() const
};
// multiplier helps fill in all the x's needed
- const unsigned end{int(c_interp * (p2_x - p1_x)) + 1u};
+ const unsigned end = (unsigned)(c_ * (p2_x - p1_x)) + 1;
+ const f end_(end);
for (unsigned k = 0; k <= end; k++)
{
- const double t = k / double(end);
- const double t2 = t*t;
- const double t3 = t*t*t;
+ const f t = k / end_;
+ const f t2 = t*t;
+ const f t3 = t*t*t;
- const int x = int(.5 * c * (cx[0] + cx[1] * t + cx[2] * t2 + cx[3] * t3));
- const float y = float(.5 * (cy[0] + cy[1] * t + cy[2] * t2 + cy[3] * t3));
+ const unsigned x = unsigned(f(.5) * c * (cx[0] + cx[1] * t + cx[2] * t2 + cx[3] * t3));
+ const float y = (float)(f(.5) * (cy[0] + cy[1] * t + cy[2] * t2 + cy[3] * t3));
- if (unsigned(x) < value_count)
+ if (x < value_count)
data[x] = y;
}
}
}
- double maxy = max_output();
+ float maxy = (float)max_output();
float last = 0;
#ifdef __clang__
# pragma clang diagnostic push
-# pragma clang diagnostic ignored "-Wfloat-equal"
+# pragma clang diagnostic ignored "-Wfloat-equal" // stupid clang
#endif
- for (unsigned i = 0; i < unsigned(value_count); i++)
+ for (unsigned i = 0; i < value_count; i++)
{
if (data[i] == magic_fill_value)
data[i] = last;
- data[i] = clamp(data[i], 0, (float)maxy);
+ data[i] = clamp(data[i], 0, maxy);
last = data[i];
}
@@ -242,58 +241,72 @@ void spline::update_interp_data() const
void spline::remove_point(int i)
{
- QMutexLocker foo(&mtx);
+ std::shared_ptr<settings> S;
+ {
+ QMutexLocker foo(&mtx);
+ S = s;
- const int sz = element_count(points, max_input());
+ const int sz = element_count(points, max_input());
- if (i >= 0 && i < sz)
- {
- points.erase(points.begin() + i);
- s->points = points;
- validp = false;
+ if (i >= 0 && i < sz)
+ {
+ points.erase(points.begin() + i);
+ s->points = points;
+ update_interp_data();
+ }
}
+
+ emit S->recomputed();
}
void spline::add_point(QPointF pt)
{
- QMutexLocker foo(&mtx);
-
- points.push_back(pt);
- std::stable_sort(points.begin(), points.end(), sort_fn);
- s->points = points;
- validp = false;
+ std::shared_ptr<settings> S;
+ {
+ QMutexLocker foo(&mtx);
+ S = s;
+ points.push_back(pt);
+ std::stable_sort(points.begin(), points.end(), sort_fn);
+ s->points = points;
+ update_interp_data();
+ }
+ emit S->recomputed();
}
void spline::add_point(double x, double y)
{
- add_point(QPointF(x, y));
+ add_point({ x, y });
}
void spline::move_point(int idx, QPointF pt)
{
- QMutexLocker foo(&mtx);
+ std::shared_ptr<settings> S;
+ {
+ QMutexLocker foo(&mtx);
+ S = s;
- const int sz = element_count(points, max_input());
+ const int sz = element_count(points, max_input());
- if (idx >= 0 && idx < sz)
- {
- points[idx] = pt;
- std::stable_sort(points.begin(), points.end(), sort_fn);
- s->points = points;
- validp = false;
+ if (idx >= 0 && idx < sz)
+ {
+ points[idx] = pt;
+ std::stable_sort(points.begin(), points.end(), sort_fn);
+ s->points = points;
+ update_interp_data();
+ }
}
+ emit S->recomputed();
}
-const base_spline_::points_t& spline::get_points() const
+const points_t& spline::get_points() const
{
- QMutexLocker foo(&mtx);
return points;
}
int spline::get_point_count() const
{
QMutexLocker foo(&mtx);
- return element_count(points, s->opts.clamp_x_);
+ return element_count(points, clamp_x);
}
void spline::reload()
@@ -308,66 +321,69 @@ void spline::save()
s->b->save();
}
+void spline::invalidate_settings_()
+{
+ points = s->points;
+ clamp_x = s->opts.clamp_x_;
+ clamp_y = s->opts.clamp_y_;
+ update_interp_data();
+}
+
void spline::invalidate_settings()
{
+ std::shared_ptr<settings> S;
{
QMutexLocker l(&mtx);
- validp = false;
- points = s->points;
+ S = s;
+ invalidate_settings_();
}
- emit s->recomputed();
+ emit S->recomputed();
}
void spline::set_bundle(bundle b, const QString& axis_name, Axis axis)
{
- QMutexLocker l(&mtx);
+ if (!b)
+ b = make_bundle({});
+
+ std::shared_ptr<settings> S;
- // gets called from ctor hence the need for nullptr checks
- // the sentinel settings/bundle objects don't need any further branching once created
- if (!s || s->b != b)
{
- disconnect_signals();
+ QMutexLocker l(&mtx);
- if (!b)
- b = make_bundle(QString{});
+ disconnect_signals();
s = std::make_shared<settings>(b, axis_name, axis);
-
- conn_changed = QObject::connect(b.get(), &bundle_::changed,
- s.get(), [&] { invalidate_settings(); }, Qt::QueuedConnection);
- // this isn't strictly necessary for the spline but helps the widget
- conn_maxx = QObject::connect(&s->opts.clamp_x_, value_::value_changed<int>(),
- ctx.get(), [&](double) { invalidate_settings(); }, Qt::QueuedConnection);
- conn_maxy = QObject::connect(&s->opts.clamp_y_, value_::value_changed<int>(),
- ctx.get(), [&](double) { invalidate_settings(); }, Qt::QueuedConnection);
-
- invalidate_settings();
+ invalidate_settings_();
+ S = s;
+
+ conn_points = QObject::connect(&s->points, value_::value_changed<QList<QPointF>>(),
+ ctx.get(), [this] { invalidate_settings(); }, Qt::DirectConnection);
+ conn_maxx = QObject::connect(&s->opts.clamp_x_, value_::value_changed<int>(),
+ ctx.get(), [this](double) { invalidate_settings(); }, Qt::DirectConnection);
+ conn_maxy = QObject::connect(&s->opts.clamp_y_, value_::value_changed<int>(),
+ ctx.get(), [this](double) { invalidate_settings(); }, Qt::DirectConnection);
}
+
+ emit S->recomputed();
}
double spline::max_input() const
{
QMutexLocker l(&mtx);
- using m = axis_opts::max_clamp;
- const value<m>& clamp = s->opts.clamp_x_;
- if (clamp == m::x1000 && !points.empty())
- return points[points.size() - 1].x();
- return std::fabs(clamp.to<double>());
+ if (clamp_x == axis_opts::x1000)
+ return std::fmax(1, points.empty() ? 0 : points[points.size() - 1].x());
+ return std::fabs((double)clamp_x);
}
double spline::max_output() const
{
QMutexLocker l(&mtx);
- using m = axis_opts::max_clamp;
- const value<m>& clamp = s->opts.clamp_y_;
- if (clamp == m::x1000 && !points.empty())
- return points[points.size() - 1].y();
- return std::fabs(clamp.to<double>());
+ if (clamp_y == axis_opts::x1000 && !points.empty())
+ return std::fmax(1, points.empty() ? 0 : points[points.size() - 1].y());
+ return std::fabs((double)clamp_y);
}
void spline::ensure_valid(points_t& list) const
{
- QMutexLocker foo(&mtx);
-
std::stable_sort(list.begin(), list.end(), sort_fn);
const unsigned sz = (unsigned)list.size();
@@ -438,14 +454,15 @@ std::shared_ptr<const base_settings> spline::get_settings() const
double spline::bucket_size_coefficient(const QList<QPointF>& points) const
{
constexpr double eps = 1e-4;
-
const double maxx = max_input();
if (maxx < eps)
return 0;
- // needed to fill the buckets up to the last control point.
- // space between that point and max_x doesn't matter.
+ // buckets are only used between 0 and the rightmost point's
+ // x coordinate. even if `clamp_x' is set to a much larger value,
+ // buckets retain more precision lower the x coordinate of the
+ // last point.
const int sz = element_count(points, maxx);
const double last_x = sz ? points[sz - 1].x() : maxx;
@@ -455,19 +472,17 @@ double spline::bucket_size_coefficient(const QList<QPointF>& points) const
void spline::disconnect_signals()
{
- if (conn_changed)
+ if (conn_points)
{
- QObject::disconnect(conn_changed); conn_changed = {};
+ QObject::disconnect(conn_points); conn_points = {};
QObject::disconnect(conn_maxx); conn_maxx = {};
QObject::disconnect(conn_maxy); conn_maxy = {};
}
}
settings::settings(bundle const& b, const QString& axis_name, Axis idx):
- b(b ? b : make_bundle("")),
+ b(b ? b : make_bundle({})),
opts(axis_name, idx)
{}
-settings::~settings() = default;
-
} // ns spline_detail
diff --git a/spline/spline.hpp b/spline/spline.hpp
index e79dd8cc..a78db8dd 100644
--- a/spline/spline.hpp
+++ b/spline/spline.hpp
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, Stanislaw Halik <sthalik@misaki.pl>
+/* Copyright (c) 2012-2019, Stanislaw Halik <sthalik@misaki.pl>
* Permission to use, copy, modify, and/or distribute this
* software for any purpose with or without fee is hereby granted,
@@ -17,7 +17,6 @@
#include <vector>
#include <limits>
#include <memory>
-#include <functional>
#include <QObject>
#include <QPointF>
@@ -26,6 +25,7 @@
namespace spline_detail {
+using points_t = QList<QPointF>;
using namespace options;
class OTR_SPLINE_EXPORT base_settings : public QObject
@@ -49,10 +49,9 @@ public:
struct OTR_SPLINE_EXPORT base_spline_
{
virtual ~base_spline_();
- base_spline_& operator=(const base_spline_&) = default;
- virtual float get_value(double x) = 0;
- virtual float get_value_no_save(double x) const = 0;
+ virtual double get_value(double x) const = 0;
+ virtual double get_value_no_save(double x) const = 0;
[[nodiscard]] virtual bool get_last_value(QPointF& point) = 0;
virtual void set_tracking_active(bool value) = 0;
@@ -60,18 +59,14 @@ struct OTR_SPLINE_EXPORT base_spline_
virtual double max_input() const = 0;
virtual double max_output() const = 0;
- using points_t = QList<QPointF>;
-
- virtual points_t const& get_points() const = 0;
+ virtual const points_t& get_points() const = 0;
virtual int get_point_count() const = 0;
};
struct OTR_SPLINE_EXPORT spline_settings_mixin
{
- using base_settings = spline_detail::base_settings;
-
- virtual std::shared_ptr<spline_detail::base_settings> get_settings() = 0;
- virtual std::shared_ptr<const spline_detail::base_settings> get_settings() const = 0;
+ virtual std::shared_ptr<base_settings> get_settings() = 0;
+ virtual std::shared_ptr<const base_settings> get_settings() const = 0;
virtual ~spline_settings_mixin();
};
@@ -94,31 +89,34 @@ struct OTR_SPLINE_EXPORT base_spline : base_spline_, spline_modify_mixin, spline
class OTR_SPLINE_EXPORT spline : public base_spline
{
+ using f = float;
+
double bucket_size_coefficient(const QList<QPointF>& points) const;
void update_interp_data() const;
- float get_value_internal(int x) const;
+ double get_value_internal(int x) const;
static bool sort_fn(const QPointF& one, const QPointF& two);
- static QPointF ensure_in_bounds(const QList<QPointF>& points, int i);
+ static void ensure_in_bounds(const QList<QPointF>& points, int i, f& x, f& y);
static int element_count(const QList<QPointF>& points, double max_input);
void disconnect_signals();
+ void invalidate_settings_();
mutex mtx { mutex::Recursive };
- std::shared_ptr<spline_detail::settings> s;
- QMetaObject::Connection conn_changed, conn_maxx, conn_maxy;
- mutable std::vector<float> data = std::vector<float>(value_count, float(-16));
- mutable QPointF last_input_value;
+ std::shared_ptr<settings> s;
+ QMetaObject::Connection conn_points, conn_maxx, conn_maxy;
std::shared_ptr<QObject> ctx { std::make_shared<QObject>() };
- // cached s->points
+ mutable QPointF last_input_value{-1, -1};
+ mutable std::vector<float> data = std::vector<float>(value_count, magic_fill_value);
mutable points_t points;
-
- static constexpr inline std::size_t value_count = 4096;
-
+ mutable axis_opts::max_clamp clamp_x = axis_opts::x1000, clamp_y = axis_opts::x1000;
mutable bool activep = false;
- mutable bool validp = false;
+
+ static constexpr unsigned value_count = 8192;
+ static constexpr float magic_fill_value = -(1 << 24) + 1;
+ static constexpr double c_interp = 5;
public:
void invalidate_settings();
@@ -134,11 +132,10 @@ public:
spline(const QString& name, const QString& axis_name, Axis axis);
~spline() override;
- spline& operator=(const spline&) = default;
spline(const spline&) = default;
- float get_value(double x) override;
- float get_value_no_save(double x) const override;
+ double get_value(double x) const override;
+ double get_value_no_save(double x) const override;
[[nodiscard]] bool get_last_value(QPointF& point) override;
void add_point(QPointF pt) override;
@@ -147,18 +144,16 @@ public:
void remove_point(int i) override;
void clear() override;
- points_t const& get_points() const override;
+ const points_t& get_points() const override;
void set_tracking_active(bool value) override;
bundle get_bundle();
void ensure_valid(points_t& in_out) const;
- std::shared_ptr<spline_detail::base_settings> get_settings() override;
- std::shared_ptr<const spline_detail::base_settings> get_settings() const override;
+ std::shared_ptr<base_settings> get_settings() override;
+ std::shared_ptr<const base_settings> get_settings() const override;
int get_point_count() const override;
-
- using settings = spline_detail::settings;
};
} // ns spline_detail