summaryrefslogtreecommitdiffhomepage
path: root/spline/spline.hpp
blob: 76ac79b812fc41f3fc4574bcb45ac3e63720710c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
/* 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 copyright notice and this permission
 * notice appear in all copies.
 */

#pragma once

#include "options/options.hpp"
#include "axis-opts.hpp"
#include "export.hpp"
#include "compat/mutex.hpp"

#include <cstddef>
#include <vector>
#include <limits>
#include <memory>

#include <QObject>
#include <QPointF>
#include <QString>
#include <QMetaObject>

namespace spline_detail {

using points_t = QList<QPointF>;
using namespace options;

class OTR_SPLINE_EXPORT base_settings : public QObject
{
    Q_OBJECT

signals:
    void recomputed() const;
};

class OTR_SPLINE_EXPORT settings final : public base_settings
{
public:
    bundle b;
    value<QList<QPointF>> points { b, "points", {} };
    axis_opts opts;
    settings(bundle const& b, const QString& axis_name, Axis idx);
    ~settings() override;
};

struct OTR_SPLINE_EXPORT base_spline_
{
    base_spline_() = default;
    virtual ~base_spline_();

    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) const = 0;

    virtual double max_input() const = 0;
    virtual double max_output() const = 0;

    virtual const points_t& get_points() const = 0;
    virtual int get_point_count() const = 0;

    base_spline_(const base_spline_&) = default;
    base_spline_& operator=(const base_spline_&) = default;
};

struct OTR_SPLINE_EXPORT spline_settings_mixin
{
    virtual std::shared_ptr<base_settings> get_settings() = 0;
    virtual std::shared_ptr<const base_settings> get_settings() const = 0;

    spline_settings_mixin(const spline_settings_mixin&) = default;
    spline_settings_mixin& operator=(const spline_settings_mixin&) = default;

    spline_settings_mixin() = default;
    virtual ~spline_settings_mixin();
};

struct OTR_SPLINE_EXPORT spline_modify_mixin
{
    virtual void add_point(QPointF pt) = 0;
    virtual void add_point(double x, double y) = 0;
    virtual void move_point(int idx, QPointF pt) = 0;
    virtual void remove_point(int i) = 0;
    virtual void clear() = 0;

    spline_modify_mixin(const spline_modify_mixin&) = default;
    spline_modify_mixin& operator=(const spline_modify_mixin&) = default;

    spline_modify_mixin() = default;
    virtual ~spline_modify_mixin();
};

struct OTR_SPLINE_EXPORT base_spline : base_spline_, spline_modify_mixin, spline_settings_mixin
{
    base_spline(const base_spline&) = default;
    base_spline& operator=(const base_spline&) = default;

    base_spline() = default;
    ~base_spline() override;
};

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;
    double get_value_internal(int x) const;
    static bool sort_fn(QPointF one, QPointF two);

    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<settings> s;
    QMetaObject::Connection conn_points, conn_maxx, conn_maxy;

    std::shared_ptr<QObject> ctx { std::make_shared<QObject>() };

    mutable QPointF last_input_value{-1, -1};
    mutable std::vector<float> data = std::vector<float>(value_count, magic_fill_value);
    mutable points_t points;
    mutable axis_opts::max_clamp clamp_x = axis_opts::x1000, clamp_y = axis_opts::x1000;
    mutable bool activep = 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();

    void reload();
    void save();
    void set_bundle(bundle b, const QString& axis_name, Axis axis);

    double max_input() const override;
    double max_output() const override;

    spline();
    spline(const QString& name, const QString& axis_name, Axis axis);
    ~spline() override;

    spline(const spline&) = default;

    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;
    void add_point(double x, double y) override;
    void move_point(int idx, QPointF pt) override;
    void remove_point(int i) override;
    void clear() override;

    const points_t& get_points() const override;

    void set_tracking_active(bool value) const override;
    bundle get_bundle();
    void ensure_valid(points_t& in_out) const;

    std::shared_ptr<base_settings> get_settings() override;
    std::shared_ptr<const base_settings> get_settings() const override;

    int get_point_count() const override;
};

} // ns spline_detail

using spline = spline_detail::spline;