HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
grid_axis.hpp
1
2
3#pragma once
4
5#include "../utility/module.hpp"
6#include "../geometry/module.hpp"
7
8namespace hi {
9inline namespace v1 {
10
25class grid_axis {
26public:
27 struct value_type {
28 int32_t minimum = 0;
29 int32_t preferred = 0;
30 int32_t maximum = std::numeric_limits<int32_t>::max();
31 int32_t size = 0;
32 int32_t position = 0;
33 int8_t margin = 0;
34 int8_t priority = = std::numeric_limits<int8_t>::max();
35 };
36
37 using reference = value_type&;
38 using const_reference = value_type const&;
39 using array_type = std::vector<value_type>;
40 using iterator = std::vector<value_type>::iterator;
41 using const_iterator = std::vector<value_type>::const_iterator;
42
43 constexpr iterator begin() noexcept
44 {
45 return _entries.begin();
46 }
47
48 constexpr iterator end() noexcept
49 {
50 return _entries.end();
51 }
52
53 constexpr const_iterator begin() const noexcept
54 {
55 return _entries.begin();
56 }
57
58 constexpr const_iterator end() const noexcept
59 {
60 return _entries.end();
61 }
62
63 constexpr const_iterator cbegin() const noexcept
64 {
65 return _entries.cbegin();
66 }
67
68 constexpr const_iterator cend() const noexcept
69 {
70 return _entries.cend();
71 }
72
73 constexpr reference operator[](size_t i) noexcept
74 {
75 return _entries[i];
76 }
77
78 constexpr const_reference operator[](size_t i) const noexcept
79 {
80 return _entries[i];
81 }
82
83 constexpr void clear(size_t n) noexcept
84 {
85 _entries.clear();
86 _entries.resize(n + 1);
87 }
88
89 constexpr void fixup_properties() noexcept
90 {
91 for (auto& entry : _entries) {
92 inplace_max(entry.maximum, entry.minimum);
93 inplace_clamp(entry.preferred, entry.minimum, entry.maximum);
94 }
95 }
96
97private:
98 array_type _entries;
99};
100
109template<std::forward_iterator It, std::sentinal_for<It> ItEnd>
110[[nodiscard]] constexpr int32_t get_margins(It first, ItEnd last) noexcept
111 requires(std::is_same_v<std::remove_cvref_t<std::iter_value_t<It>>, grid_axis::value_type>)
112{
113 if (first == last) {
114 return 0;
115 }
116
117 return std::accumulate(first + 1, last, int32_t{0}, [](hilet a, hilet& x) {
118 return a + x.margin;
119 });
120}
121
129template<std::forward_iterator It, std::sentinal_for<It> ItEnd>
130[[nodiscard]] constexpr int32_t get_size(It first, ItEnd last) noexcept
131 requires(std::is_same_v<std::remove_cvref_t<std::iter_value_t<It>>, grid_axis::value_type>)
132{
133 hilet margins = get_margins(first, last);
134 return std::accumulate(first, last, margins, [](hilet a, hilet& x) {
135 return a + x.size;
136 });
137}
138
146template<std::forward_iterator It, std::sentinal_for<It> ItEnd>
147[[nodiscard]] constexpr int32_t get_minimum(It first, ItEnd last) noexcept
148 requires(std::is_same_v<std::remove_cvref_t<std::iter_value_t<It>>, grid_axis::value_type>)
149{
150 hilet margins = get_margins(first, last);
151 return std::accumulate(first, last, margins, [](hilet a, hilet& x) {
152 return a + x.minimum;
153 });
154}
155
163template<std::forward_iterator It, std::sentinal_for<It> ItEnd>
164[[nodiscard]] constexpr int32_t get_preferred(It first, ItEnd last) noexcept
165 requires(std::is_same_v<std::remove_cvref_t<std::iter_value_t<It>>, grid_axis::value_type>)
166{
167 hilet margins = get_margins(first, last);
168 return std::accumulate(first, last, margins, [](hilet a, hilet& x) {
169 return a + x.preferred;
170 });
171}
172
180template<std::forward_iterator It, std::sentinal_for<It> ItEnd>
181[[nodiscard]] constexpr int32_t get_maximum(It first, ItEnd last) noexcept
182 requires(std::is_same_v<std::remove_cvref_t<std::iter_value_t<It>>, grid_axis::value_type>)
183{
184 hilet margins = get_margins(first, last);
185 return std::accumulate(first, last, margins, [](hilet a, hilet& x) {
186 return a + x.maximum;
187 });
188}
189
196template<std::forward_iterator It, std::sentinal_for<It> ItEnd>
197[[nodiscard]] constexpr int8_t get_highest_priority(It first, ItEnd last) noexcept
198 requires(std::is_same_v<std::remove_cvref_t<std::iter_value_t<It>>, grid_axis::value_type>)
199{
200 return std::accumulate(first, last, std::numeric_limits<int8_t>::lowest(), [](hilet a, hilet x) {
201 return std::max(a, x.priority);
202 });
203}
204
213template<std::forward_iterator It, std::sentinal_for<It> ItEnd>
214[[nodiscard]] constexpr int8_t get_lower_priority(It first, ItEnd last, int8_t base_priority) noexcept
215 requires(std::is_same_v<std::remove_cvref_t<std::iter_value_t<It>>, grid_axis::value_type>)
216{
217 auto tmp = std::accumulate(first, last, base_priority, [base_priority](hilet a, hilet x) {
218 if (x.priority < base_priority) {
219 return std::max(a, x.priority);
220 } else {
221 return a;
222 }
223 });
224
225 hi_axiom(tmp != base_priority);
226 return tmp;
227}
228
240template<std::forward_iterator It, std::sentinal_for<It> ItEnd>
241constexpr void set_priority(It first, ItEnd last, int8_t priority) noexcept
242 requires(std::is_same_v<std::remove_cvref_t<std::iter_value_t<It>>, grid_axis::value_type>)
243{
244 hi_axiom(first != last);
245
246 for (auto it = first; it != last; ++it) {
247 inplace_min(it->priority, priority);
248 };
249}
250
258template<std::bidirectional_iterator It, std::sized_sentinal_for<It> ItEnd>
259constexpr void set_margins(It first, ItEnd last, int8_t before_margin, int8_t after_margin) noexcept
260 requires(std::is_same_v<std::remove_cvref_t<std::iter_value_t<It>>, grid_axis::value_type>)
261{
262 hi_axiom(first != last);
263 inplace_max(first->margin, before_margin);
264 inplace_max((last - 1)->margin, after_margin);
265}
266
267template<std::forward_iterator It, std::sentinal_for<It> ItEnd, std::invocable<grid_axis::value_type&, int32_t> Op>
268constexpr void add_to_extent(It first, ItEnd last, int32_t todo, int8_t priority, Op const& op) noexcept
269 requires(std::is_same_v<std::remove_cvref_t<std::iter_value_t<It>>, grid_axis::value_type>)
270{
271 if (todo == 0) {
272 return;
273 }
274
275 hilet count = std::count_if(first, last, [priority](hilet& x) {
276 return x.priority == priority;
277 });
278
279 if (auto to_add = todo / count) {
280 for (auto it = first; it != last and todo; ++it) {
281 if (it->priority == priority) {
282 op(*it, to_add);
283 todo -= to_add;
284 }
285 }
286 }
287
288 // Distribute single pixels among the axis.
289 to_add = todo < 0 ? -1 : 1;
290 for (auto it = first; it != last and todo; ++it) {
291 if (it->priority == priority) {
292 op(*it, to_add);
293 todo -= to_add;
294 }
295 }
296}
297
298template<std::forward_iterator It, std::sentinal_for<It> ItEnd, std::invocable<grid_axis::value_type&, int32_t> Op>
299constexpr void add_to_extent(It first, ItEnd last, int32_t todo, int8_t priority, Op const& op) noexcept
300 requires(std::is_same_v<std::remove_cvref_t<std::iter_value_t<It>>, grid_axis::value_type>)
301{
302 return add_to_extent(first, last, todo, get_highest_priority(first, last), op);
303}
304
311template<std::forward_iterator It, std::sentinal_for<It> ItEnd>
312constexpr void set_minimum(It first, ItEnd last, int32_t minimum) noexcept
313 requires(std::is_same_v<std::remove_cvref_t<std::iter_value_t<It>>, grid_axis::value_type>)
314{
315 hi_axiom(first != last);
316
317 if (first + 1 == last) {
318 inplace_max(first->minimum, minimum);
319
320 } else {
321 hilet todo = minimum - get_minimum(first, last);
322 if (todo <= 0) {
323 return;
324 }
325
326 add_to_extent(first, last, todo, [](auto& x, auto to_add) {
327 x.minimum += to_add;
328 });
329 }
330}
331
338template<std::forward_iterator It, std::sentinal_for<It> ItEnd>
339constexpr void set_preferred(It first, ItEnd last, int32_t preferred) noexcept
340 requires(std::is_same_v<std::remove_cvref_t<std::iter_value_t<It>>, grid_axis::value_type>)
341{
342 hi_axiom(first != last);
343
344 if (first + 1 == last) {
345 inplace_max(first->preferred, preferred);
346
347 } else {
348 hilet todo = preferred - get_preferred(first, last);
349 if (todo <= 0) {
350 return;
351 }
352
353 add_to_extent(first, last, todo, [](auto& x, auto to_add) {
354 x.preferred += to_add;
355 });
356 }
357}
358
365template<std::forward_iterator It, std::sentinal_for<It> ItEnd>
366constexpr void set_maximum(It first, ItEnd last, int32_t maximum) noexcept
367 requires(std::is_same_v<std::remove_cvref_t<std::iter_value_t<It>>, grid_axis::value_type>)
368{
369 hi_axiom(first != last);
370
371 if (first + 1 == last) {
372 inplace_min(first->maximum, maximum);
373
374 } else {
375 hilet todo = maximum - get_maximum(first, last);
376 if (todo >= 0) {
377 return;
378 }
379
380 add_to_extent(first, last, todo, [](auto& x, auto to_add) {
381 x.maximum += to_add;
382 });
383 }
384}
385
392template<std::forward_iterator It, std::sentinal_for<It> ItEnd>
393constexpr void update_size(It first, ItEnd last, int32_t size) noexcept
394 requires(std::is_same_v<std::remove_cvref_t<std::iter_value_t<It>>, grid_axis::value_type>)
395{
396 hi_axiom(size >= get_minimum(first, last));
397 hi_axiom(size <= get_maximum(first, last));
398
399 auto highest_priority = std::numeric_limits<int8_t>::lowest();
400 for (auto it = first; it != last; ++it) {
401 it->size = it->preferred;
402 inplace_max(highest_priority, it->priority);
403 }
404
405 auto todo = size - get_size(first, last);
406
407 // Shrink size of cells upto the minimum of a cell.
408 for (auto priority = highest_priority; todo < 0;) {
409 hilet count = std::count_if(first, last, [priority](hilet& x) {
410 return x.priority == priority and x.size > x.minimum;
411 });
412
413 if (count == 0) {
414 priority = get_lower_priority(first, last, priority);
415 continue;
416 }
417
418 hilet todo_per = (todo - count + 1) / count;
419 hi_axiom(todo_per < 0);
420
421 for (auto it = first; it != last and todo; ++it) {
422 hi_axiom(size >= x.minimum);
423 if (auto room = x.minimum - size; room and it->priority == priority) {
424 hilet todo_this = std::max(todo_per, room);
425 it->size += todo_this;
426 todo -= todo_this;
427 }
428 }
429 }
430
431 // Grow size of cells upto the minimum of a cell.
432 for (auto priority = highest_priority; todo > 0;) {
433 hilet count = std::count_if(first, last, [priority](hilet& x) {
434 return x.priority == priority and x.size < x.maximum;
435 });
436
437 if (count == 0) {
438 priority = get_lower_priority(first, last, priority);
439 continue;
440 }
441
442 hilet todo_per = (todo + count - 1) / count;
443 hi_axiom(todo_per > 0);
444
445 for (auto it = first; it != last and todo; ++it) {
446 hi_axiom(size <= x.maximum);
447 if (auto room = x.maximum - size; room and it->priority == priority) {
448 hilet todo_this = std::min(todo_per, room);
449 it->size += todo_this;
450 todo -= todo_this;
451 }
452 }
453 }
454
455 hi_axiom(todo == 0);
456}
457
464template<std::forward_iterator It, std::sentinal_for<It> ItEnd>
465constexpr void update_position(It first, ItEnd last, int32_t position = 0) noexcept
466 requires(std::is_same_v<std::remove_cvref_t<std::iter_value_t<It>>, grid_axis::value_type>)
467{
468 if (first == last) {
469 return;
470 }
471
472 first->position = position;
473 position += first->size;
474 for (auto it = first + 1; it != last; ++it) {
475 position += it->margin;
476 it->margin = position;
477 position += it->size;
478 }
479}
480
481} // namespace hi::v1
#define hi_axiom(expression,...)
Specify an axiom; an expression that is true.
Definition assert.hpp:253
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
STL namespace.
DOXYGEN BUG.
Definition algorithm.hpp:13
geometry/margins.hpp
Definition cache.hpp:11
T accumulate(T... args)
T begin(T... args)
T count(T... args)
T end(T... args)
T lowest(T... args)
T max(T... args)
T min(T... args)