Loading...
Searching...
No Matches
score-plugin-curve/Curve/Envelope.hpp
1#pragma once
2#include <ossia/detail/pod_vector.hpp>
3
4#include <QLineF>
5#include <QPointF>
6
7#include <score_plugin_curve_export.h>
8
9#include <algorithm>
10#include <cstddef>
11#include <limits>
12#include <tuple>
13#include <utility>
14#include <vector>
15
16namespace Curve
17{
20class SCORE_PLUGIN_CURVE_EXPORT MinMaxPyramid
21{
22public:
23 float value(std::size_t i) const noexcept { return m_y[i]; }
24
26 template <typename Y>
27 void build(std::size_t n, Y&& y)
28 {
29 m_y.resize(n);
30 for(std::size_t i = 0; i < n; i++)
31 m_y[i] = float(y(i));
32 buildLevels();
33 }
34
36 std::pair<float, float> range(std::size_t first, std::size_t last) const noexcept;
37
38private:
39 void buildLevels();
40
41 struct Extremes
42 {
43 float min, max;
44 };
45 ossia::pod_vector<float> m_y;
46 std::vector<ossia::pod_vector<Extremes>> m_levels;
47};
48
49namespace detail
50{
53template <typename X>
54std::size_t seek(const X& x, std::size_t n, std::size_t from, double v)
55{
56 std::size_t lo = from, step = 1;
57 std::size_t hi = from;
58 while(hi < n && x(hi) < v)
59 {
60 lo = hi + 1;
61 hi = from + step;
62 step *= 2;
63 }
64 hi = std::min(hi, n);
65 while(lo < hi)
66 {
67 const std::size_t mid = lo + (hi - lo) / 2;
68 if(x(mid) < v)
69 lo = mid + 1;
70 else
71 hi = mid;
72 }
73 return lo;
74}
75}
76
80template <typename X, typename Y>
82 std::size_t n, const X& x, const Y& y, const MinMaxPyramid& pyramid,
83 std::size_t pyramid_offset, double x0, double x1, int columns,
84 std::vector<QPointF>& out)
85{
86 out.clear();
87 if(n == 0 || columns <= 0 || !(x1 > x0))
88 return;
89
90 const std::size_t begin = detail::seek(x, n, 0, x0);
91 const std::size_t end = std::min(n, detail::seek(x, n, begin, x1) + 1);
92 if(begin > 0)
93 out.emplace_back(x(begin - 1), y(begin - 1));
94
95 const double width = (x1 - x0) / columns;
96 std::size_t i = begin;
97 for(int c = 0; c < columns && i < end; c++)
98 {
99 const double col_end = c + 1 == columns ? x1 : x0 + (c + 1) * width;
100 const std::size_t j = std::min(end, detail::seek(x, n, i, col_end));
101 if(j - i <= 2)
102 {
103 for(std::size_t k = i; k < j; k++)
104 out.emplace_back(x(k), y(k));
105 }
106 else
107 {
108 const auto [lo, hi] = pyramid.range(pyramid_offset + i, pyramid_offset + j);
109 const double first = y(i), last = y(j - 1);
110 const double mid = x0 + (c + 0.5) * width;
111 out.emplace_back(x(i), first);
112 // In the order the column goes: down then up, or up then down.
113 if(first <= last)
114 {
115 out.emplace_back(mid, lo);
116 out.emplace_back(mid, hi);
117 }
118 else
119 {
120 out.emplace_back(mid, hi);
121 out.emplace_back(mid, lo);
122 }
123 out.emplace_back(x(j - 1), last);
124 }
125 i = j;
126 }
127 for(; i < end; i++)
128 out.emplace_back(x(i), y(i));
129 if(end < n)
130 out.emplace_back(x(end), y(end));
131}
132
137template <typename X, typename Y>
139 std::size_t n, const X& x, const Y& y, const MinMaxPyramid& pyramid,
140 std::size_t pyramid_offset, double x0, double width, int columns,
141 std::vector<QLineF>& out)
142{
143 out.clear();
144 if(n == 0 || columns <= 0 || !(width > 0.))
145 return;
146 out.reserve(columns);
147
148 // The line between points k - 1 and k at v, for 0 < k < n.
149 auto at = [&](std::size_t k, double v) {
150 const double xa = x(k - 1), xb = x(k);
151 const double t = xb > xa ? (v - xa) / (xb - xa) : 1.;
152 return y(k - 1) + t * (y(k) - y(k - 1));
153 };
154
155 std::size_t i = detail::seek(x, n, 0, x0);
156 for(int c = 0; c < columns; c++)
157 {
158 const double a = x0 + c * width;
159 const double b = a + width;
160 const double mid = a + 0.5 * width;
161 const std::size_t j = detail::seek(x, n, i, b);
162
163 double lo = std::numeric_limits<double>::max();
164 double hi = std::numeric_limits<double>::lowest();
165 if(j > i)
166 std::tie(lo, hi) = pyramid.range(pyramid_offset + i, pyramid_offset + j);
167 if(i > 0 && i < n)
168 {
169 const double v = at(i, a);
170 lo = std::min(lo, v);
171 hi = std::max(hi, v);
172 }
173 if(j > 0 && j < n)
174 {
175 const double v = at(j, b);
176 lo = std::min(lo, v);
177 hi = std::max(hi, v);
178 }
179 if(lo <= hi)
180 out.emplace_back(mid, lo, mid, hi);
181 i = j;
182 }
183}
184
188{
189 double first{}; // in item coordinates, on the grid
190 double width{}; // in item coordinates: one device pixel
191 int count{};
192};
193SCORE_PLUGIN_CURVE_EXPORT
194PixelColumns pixelColumns(double left, double right, double devicePixelsPerUnit);
195}
Definition score-plugin-curve/Curve/Envelope.hpp:21
void build(std::size_t n, Y &&y)
y(i) for i in [0, n).
Definition score-plugin-curve/Curve/Envelope.hpp:27
std::pair< float, float > range(std::size_t first, std::size_t last) const noexcept
Over [first, last), which must not be empty.
Definition Envelope.cpp:40
Utilities and base classes for 1D curves.
Definition FocusDispatcher.hpp:12
void envelope(std::size_t n, const X &x, const Y &y, const MinMaxPyramid &pyramid, std::size_t pyramid_offset, double x0, double x1, int columns, std::vector< QPointF > &out)
Definition score-plugin-curve/Curve/Envelope.hpp:81
void envelopeColumns(std::size_t n, const X &x, const Y &y, const MinMaxPyramid &pyramid, std::size_t pyramid_offset, double x0, double width, int columns, std::vector< QLineF > &out)
Definition score-plugin-curve/Curve/Envelope.hpp:138
Definition score-plugin-curve/Curve/Envelope.hpp:188
Definition MIDISync.hpp:127