7#include "box_constraints.hpp"
8#include "box_shape.hpp"
9#include "spreadsheet_address.hpp"
20namespace hi {
inline namespace v1 {
27 size_t first_column = 0;
29 size_t last_column = 0;
31 bool beyond_maximum =
false;
32 value_type value = {};
47 std::convertible_to<value_type>
auto&& value) noexcept :
48 first_column(first_column),
50 last_column(last_column),
52 beyond_maximum(beyond_maximum),
59 constexpr void set_constraints(
box_constraints const& constraints)
noexcept
61 _constraints = constraints;
64 template<hi::axis Axis>
65 [[nodiscard]]
constexpr size_t first()
const noexcept
67 if constexpr (Axis == axis::x) {
69 }
else if constexpr (Axis == axis::y) {
76 template<hi::axis Axis>
77 [[nodiscard]]
constexpr size_t last()
const noexcept
79 if constexpr (Axis == axis::x) {
81 }
else if constexpr (Axis == axis::y) {
88 template<hi::axis Axis>
89 [[nodiscard]]
constexpr size_t span()
const noexcept
91 hi_axiom(first<Axis>() < last<Axis>());
92 return last<Axis>() - first<Axis>();
95 template<hi::axis Axis>
96 [[nodiscard]]
constexpr auto alignment()
const noexcept
98 if constexpr (Axis == axis::x) {
99 return _constraints.alignment.horizontal();
100 }
else if constexpr (Axis == axis::y) {
101 return _constraints.alignment.vertical();
107 template<hi::axis Axis>
108 [[nodiscard]]
constexpr int minimum()
const noexcept
110 if constexpr (Axis == axis::x) {
111 return _constraints.minimum.
width();
112 }
else if constexpr (Axis == axis::y) {
113 return _constraints.minimum.
height();
119 template<hi::axis Axis>
120 [[nodiscard]]
constexpr int preferred()
const noexcept
122 if constexpr (Axis == axis::x) {
123 return _constraints.preferred.
width();
124 }
else if constexpr (Axis == axis::y) {
125 return _constraints.preferred.
height();
131 template<hi::axis Axis>
132 [[nodiscard]]
constexpr int maximum()
const noexcept
134 if constexpr (Axis == axis::x) {
135 return _constraints.maximum.
width();
136 }
else if constexpr (Axis == axis::y) {
137 return _constraints.maximum.
height();
143 template<hi::axis Axis>
144 [[nodiscard]]
constexpr int margin_before(
bool forward)
const noexcept
146 if constexpr (Axis == axis::x) {
148 return _constraints.margins.left();
150 return _constraints.margins.right();
152 }
else if constexpr (Axis == axis::y) {
154 return _constraints.margins.bottom();
156 return _constraints.margins.top();
163 template<hi::axis Axis>
164 [[nodiscard]]
constexpr int margin_after(
bool forward)
const noexcept
166 if constexpr (Axis == axis::x) {
168 return _constraints.margins.right();
170 return _constraints.margins.left();
172 }
else if constexpr (Axis == axis::y) {
174 return _constraints.margins.top();
176 return _constraints.margins.bottom();
183 template<hi::axis Axis>
184 [[nodiscard]]
constexpr int padding_before(
bool forward)
const noexcept
186 if constexpr (Axis == axis::x) {
188 return _constraints.padding.left();
190 return _constraints.padding.right();
192 }
else if constexpr (Axis == axis::y) {
194 return _constraints.padding.bottom();
196 return _constraints.padding.top();
203 template<hi::axis Axis>
204 [[nodiscard]]
constexpr int padding_after(
bool forward)
const noexcept
206 if constexpr (Axis == axis::x) {
208 return _constraints.padding.right();
210 return _constraints.padding.left();
212 }
else if constexpr (Axis == axis::y) {
214 return _constraints.padding.top();
216 return _constraints.padding.bottom();
227template<hi::axis Axis,
typename T>
232 using value_type = T;
233 using alignment_type = std::conditional_t<axis == axis::y, vertical_alignment, horizontal_alignment>;
293 using iterator = constraint_vector::iterator;
294 using const_iterator = constraint_vector::const_iterator;
295 using reverse_iterator = constraint_vector::reverse_iterator;
296 using reference = constraint_vector::reference;
297 using const_reference = constraint_vector::const_reference;
305 [[nodiscard]] constexpr friend
bool
315 _constraints(num), _forward(forward)
317 for (
hilet& cell : cells) {
318 construct_simple_cell(cell);
322 for (
hilet& cell : cells) {
323 construct_span_cell(cell);
328 [[nodiscard]]
constexpr int margin_before() const noexcept
330 return empty() ? 0 : _forward ?
front().margin_before :
back().margin_before;
333 [[nodiscard]]
constexpr int margin_after() const noexcept
335 return empty() ? 0 : _forward ?
back().margin_after :
front().margin_after;
338 [[nodiscard]]
constexpr int padding_before() const noexcept
340 return empty() ? 0 : _forward ?
front().padding_before :
back().padding_before;
343 [[nodiscard]]
constexpr int padding_after() const noexcept
345 return empty() ? 0 : _forward ?
back().padding_after :
front().padding_after;
365 [[nodiscard]]
constexpr int position(cell_type
const& cell)
const noexcept
367 return position(cell.first<
axis>(), cell.last<
axis>());
370 [[nodiscard]]
constexpr int extent(cell_type
const& cell)
const noexcept
372 return extent(cell.first<
axis>(), cell.last<
axis>());
375 [[nodiscard]]
constexpr std::optional<int> guideline(cell_type
const& cell)
const noexcept
377 if (cell.span<
axis>() == 1) {
378 return guideline(cell.first<
axis>());
406 constexpr void layout(
int new_position,
int new_extent, std::optional<int> external_guideline,
int guideline_width)
noexcept
409 for (
auto& constraint : _constraints) {
410 constraint.extent = constraint.preferred;
414 auto [total_extent, count] = layout_shrink(
begin(),
end());
415 while (total_extent > new_extent and count != 0) {
417 std::tie(total_extent, count) = layout_shrink(
begin(),
end(), total_extent - new_extent, count);
422 while (total_extent < new_extent and count != 0) {
424 std::tie(total_extent, count) = layout_expand(
begin(),
end(), new_extent - total_extent, count);
428 if (total_extent < new_extent) {
431 return item.beyond_maximum;
434 hilet todo = new_extent - total_extent;
435 hilet per_extent = narrow_cast<int>((todo + count - 1) / count);
436 for (
auto& constraint : _constraints) {
437 if (constraint.beyond_maximum) {
438 constraint.extent += per_extent;
446 if (total_extent < new_extent and not
empty()) {
448 front().extent += new_extent - total_extent;
452 layout_position(
begin(),
end(), new_position, guideline_width);
454 layout_position(
rbegin(),
rend(), new_position, guideline_width);
457 if (external_guideline and
size() == 1) {
460 front().guideline = *external_guideline;
466 [[nodiscard]]
constexpr size_t size() const noexcept
468 return _constraints.
size();
473 [[nodiscard]]
constexpr bool empty() const noexcept
475 return _constraints.
empty();
480 [[nodiscard]]
constexpr iterator
begin() noexcept
482 return _constraints.
begin();
487 [[nodiscard]]
constexpr const_iterator
begin() const noexcept
489 return _constraints.
begin();
494 [[nodiscard]]
constexpr const_iterator
cbegin() const noexcept
496 return _constraints.
cbegin();
501 [[nodiscard]]
constexpr iterator
end() noexcept
503 return _constraints.
end();
508 [[nodiscard]]
constexpr const_iterator
end() const noexcept
510 return _constraints.
end();
515 [[nodiscard]]
constexpr const_iterator
cend() const noexcept
517 return _constraints.
cend();
522 [[nodiscard]]
constexpr reverse_iterator
rbegin() noexcept
524 return _constraints.
rbegin();
529 [[nodiscard]]
constexpr reverse_iterator
rend() noexcept
531 return _constraints.
rend();
540 [[nodiscard]]
constexpr reference
operator[](
size_t index)
noexcept
543 return _constraints[index];
552 [[nodiscard]]
constexpr const_reference
operator[](
size_t index)
const noexcept
555 return _constraints[index];
563 [[nodiscard]]
constexpr reference
front() noexcept
566 return _constraints.
front();
574 [[nodiscard]]
constexpr const_reference
front() const noexcept
577 return _constraints.
front();
585 [[nodiscard]]
constexpr reference
back() noexcept
588 return _constraints.
back();
596 [[nodiscard]]
constexpr const_reference
back() const noexcept
599 return _constraints.
back();
611 constraint_vector _constraints = {};
615 bool _forward =
true;
634 layout_shrink(const_iterator first, const_iterator last,
int extra = 0,
size_t count = 1) noexcept
641 hilet extra_per = narrow_cast<int>((extra + count - 1) / count);
644 auto new_count = 0_uz;
645 for (
auto it = first_; it != last_; ++it) {
646 it->extent = it->extent -
std::max(extra_per, it->extent - it->minimum);
649 new_extent += it->margin_before;
651 new_extent += it->extent;
653 if (it->extent > it->minimum) {
658 return {new_extent, new_count};
678 layout_expand(const_iterator first, const_iterator last,
int extra = 0,
size_t count = 1) noexcept
685 hilet extra_per = narrow_cast<int>((extra + count - 1) / count);
688 auto new_count = 0_uz;
689 for (
auto it = first_; it != last_; ++it) {
690 it->extent = it->extent +
std::min(extra_per, it->maximum - it->extent);
693 new_extent += it->margin_before;
695 new_extent += it->extent;
697 if (it->extent < it->maximum) {
702 return {new_extent, new_count};
705 constexpr void layout_position(
auto first,
auto last,
int start_position,
int guideline_width)
noexcept
707 auto position = start_position;
708 for (
auto it = first; it != last; ++it) {
709 it->position = position;
711 it->alignment, position, position + it->extent, it->padding_before, it->padding_after, guideline_width);
713 position += it->extent;
714 position += it->margin_after;
725 constexpr void construct_simple_cell(cell_type
const& cell)
noexcept
727 inplace_max(_constraints[cell.first<axis>()].margin_before, cell.margin_before<axis>(_forward));
728 inplace_max(_constraints[cell.last<axis>() - 1].margin_after, cell.margin_after<axis>(_forward));
729 inplace_max(_constraints[cell.first<axis>()].padding_before, cell.padding_before<axis>(_forward));
730 inplace_max(_constraints[cell.last<axis>() - 1].padding_after, cell.padding_after<axis>(_forward));
732 for (
auto i = cell.first<axis>(); i != cell.last<
axis>(); ++i) {
733 _constraints[i].beyond_maximum |= cell.beyond_maximum;
736 if (cell.span<axis>() == 1) {
737 inplace_max(_constraints[cell.first<axis>()].alignment, cell.alignment<axis>());
738 inplace_max(_constraints[cell.first<axis>()].minimum, cell.minimum<axis>());
739 inplace_max(_constraints[cell.first<axis>()].preferred, cell.preferred<axis>());
740 inplace_min(_constraints[cell.first<axis>()].maximum, cell.maximum<axis>());
750 constexpr void construct_span_cell(cell_type
const& cell)
noexcept
752 if (cell.span<axis>() > 1) {
754 if (
hilet extra = cell.minimum<axis>() - span_minimum; extra > 0) {
755 hilet extra_per_cell = narrow_cast<int>((extra + cell.span<axis>() - 1) / cell.span<axis>());
756 for (
auto i = cell.first<axis>(); i != cell.last<
axis>(); ++i) {
757 _constraints[i].minimum += extra_per_cell;
761 if (
hilet extra = cell.preferred<axis>() - span_preferred; extra > 0) {
762 hilet extra_per_cell = narrow_cast<int>((extra + cell.span<axis>() - 1) / cell.span<axis>());
763 for (
auto i = cell.first<axis>(); i != cell.last<
axis>(); ++i) {
764 _constraints[i].preferred += extra_per_cell;
768 if (
hilet extra = cell.maximum<axis>() - span_preferred; extra < 0) {
769 hilet extra_per_cell = narrow_cast<int>((extra + cell.span<axis>() - 1) / cell.span<axis>());
770 for (
auto i = cell.first<axis>(); i != cell.last<
axis>(); ++i) {
772 _constraints[i].maximum += extra_per_cell;
782 constexpr void construct_fixup() noexcept
784 for (
auto it =
begin(); it !=
end(); ++it) {
786 if (it + 1 !=
end()) {
787 it->margin_after = (it + 1)->margin_before =
std::max(it->margin_after, (it + 1)->margin_before);
791 inplace_max(it->preferred, it->minimum);
792 inplace_max(it->maximum, it->preferred);
795 if (it->padding_before + it->padding_after > it->minimum) {
796 hilet padding_diff = it->padding_after - it->padding_before;
797 hilet middle = std::clamp(it->minimum / 2 + padding_diff, 0, it->minimum);
798 it->padding_after =
middle;
799 it->padding_before = it->minimum -
middle;
815 auto r_preferred = 0;
820 r_minimum = first->minimum;
821 r_preferred = first->preferred;
822 r_maximum = first->maximum;
823 for (
auto it = first + 1; it != last; ++it) {
824 r_margin += it->margin_before;
825 r_minimum += it->minimum;
826 r_preferred += it->preferred;
827 r_maximum += it->maximum;
830 return {r_minimum + r_margin, r_preferred + r_margin, r_maximum + r_margin};
855 [[nodiscard]]
constexpr int position(const_iterator first, const_iterator last)
const noexcept
859 return first->position;
861 return (last - 1)->position;
872 [[nodiscard]]
constexpr int position(
size_t first,
size_t last)
const noexcept
886 [[nodiscard]]
constexpr int extent(const_iterator first, const_iterator last)
const noexcept
891 for (
auto it = first + 1; it != last; ++it) {
892 r += it->margin_before;
906 [[nodiscard]]
constexpr int extent(
size_t first,
size_t last)
const noexcept
913 [[nodiscard]]
constexpr std::optional<int> guideline(const_iterator it)
const noexcept
915 return it->guideline;
918 [[nodiscard]]
constexpr std::optional<int> guideline(
size_t i)
const noexcept
920 return guideline(
cbegin() + i);
934 using value_type = T;
936 using cell_type = detail::grid_layout_cell<value_type>;
938 using iterator = cell_vector::iterator;
939 using const_iterator = cell_vector::const_iterator;
940 using reference = cell_vector::reference;
941 using const_reference = cell_vector::const_reference;
949 [[nodiscard]]
constexpr friend bool operator==(
grid_layout const&,
grid_layout const&)
noexcept =
default;
951 [[nodiscard]]
constexpr bool empty()
const noexcept
953 return _cells.
empty();
956 [[nodiscard]]
constexpr size_t size()
const noexcept
958 return _cells.
size();
961 [[nodiscard]]
constexpr size_t num_columns()
const noexcept
966 [[nodiscard]]
constexpr size_t num_rows()
const noexcept
971 [[nodiscard]]
constexpr iterator begin()
noexcept
973 return _cells.
begin();
976 [[nodiscard]]
constexpr iterator end()
noexcept
981 [[nodiscard]]
constexpr const_iterator begin()
const noexcept
983 return _cells.
begin();
986 [[nodiscard]]
constexpr const_iterator end()
const noexcept
991 [[nodiscard]]
constexpr const_iterator cbegin()
const noexcept
996 [[nodiscard]]
constexpr const_iterator cend()
const noexcept
998 return _cells.
cend();
1001 [[nodiscard]]
constexpr const_reference operator[](
size_t i)
const noexcept
1006 [[nodiscard]]
constexpr reference operator[](
size_t i)
noexcept
1019 [[nodiscard]]
constexpr bool cell_in_use(
size_t first_column,
size_t first_row,
size_t last_column,
size_t last_row)
noexcept
1022 hi_axiom(first_column < last_column);
1025 for (
hilet& cell : _cells) {
1026 if (first_column >= cell.last_column) {
1029 if (last_column <= cell.first_column) {
1032 if (first_row >= cell.last_row) {
1035 if (last_row <= cell.first_row) {
1053 template<forward_of<value_type> Value>
1055 size_t first_column,
1060 bool beyond_maximum =
false) noexcept
1066 auto& r = _cells.
emplace_back(first_column, first_row, last_column, last_row, beyond_maximum, std::forward<Value>(value));
1067 update_after_insert_or_delete();
1079 template<forward_of<value_type> Value>
1080 constexpr reference
add_cell(
size_t column,
size_t row, Value&& value,
bool beyond_maximum =
false) noexcept
1082 return add_cell(column, row, column + 1, row + 1, std::forward<Value>(value), beyond_maximum);
1085 constexpr void clear() noexcept
1088 update_after_insert_or_delete();
1091 [[nodiscard]]
constexpr box_constraints constraints(
bool left_to_right)
const noexcept
1094 _row_constraints = {_cells, num_rows(),
false};
1095 _column_constraints = {_cells, num_columns(), left_to_right};
1097 auto r = box_constraints{};
1098 std::tie(r.minimum.width(), r.preferred.width(), r.maximum.width()) = _column_constraints.update_constraints();
1099 r.margins.left() = _column_constraints.margin_before();
1100 r.margins.right() = _column_constraints.margin_after();
1101 r.padding.left() = _column_constraints.padding_before();
1102 r.padding.right() = _column_constraints.padding_after();
1104 std::tie(r.minimum.height(), r.preferred.height(), r.maximum.height()) = _row_constraints.update_constraints();
1105 r.margins.bottom() = _row_constraints.margin_after();
1106 r.margins.top() = _row_constraints.margin_before();
1107 r.padding.bottom() = _row_constraints.padding_after();
1108 r.padding.top() = _row_constraints.padding_before();
1111 if (num_rows() == 1 and num_columns() == 1) {
1112 return hi::alignment{_column_constraints.
front().alignment, _row_constraints.
front().alignment};
1113 }
else if (num_rows() == 1) {
1114 return hi::alignment{_row_constraints.
front().alignment};
1115 }
else if (num_columns() == 1) {
1116 return hi::alignment{_column_constraints.
front().alignment};
1118 return hi::alignment{};
1133 _column_constraints.
layout(shape.x(), shape.width(), shape.centerline, 0);
1134 _row_constraints.
layout(shape.y(), shape.height(), shape.baseline, baseline_adjustment);
1137 for (
auto& cell : _cells) {
1138 cell.shape.rectangle = {
1139 _column_constraints.position(cell),
1140 _row_constraints.position(cell),
1141 _column_constraints.extent(cell),
1142 _row_constraints.extent(cell)};
1143 cell.shape.centerline = _column_constraints.guideline(cell);
1144 cell.shape.baseline = _row_constraints.guideline(cell);
1149 cell_vector _cells = {};
1150 size_t _num_rows = 0;
1151 size_t _num_columns = 0;
1152 mutable detail::grid_layout_axis_constraints<axis::y, value_type> _row_constraints = {};
1153 mutable detail::grid_layout_axis_constraints<axis::x, value_type> _column_constraints = {};
1159 constexpr void sort_cells() noexcept
1161 std::sort(_cells.begin(), _cells.end(), [](cell_type
const& lhs, cell_type
const& rhs) {
1162 if (lhs.first_row != rhs.first_row) {
1163 return lhs.first_row < rhs.first_row;
1165 return lhs.first_column < rhs.first_column;
1172 constexpr void update_after_insert_or_delete() noexcept
1178 for (
hilet& cell : _cells) {
1179 inplace_max(_num_rows, cell.last_row);
1180 inplace_max(_num_columns, cell.last_column);
#define hi_static_no_default(...)
This part of the code should not be reachable, unless a programming bug.
Definition assert.hpp:181
#define hi_assert(expression,...)
Assert if expression is true.
Definition assert.hpp:87
#define hi_axiom(expression,...)
Specify an axiom; an expression that is true.
Definition assert.hpp:133
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
#define hi_forward(x)
Forward a value, based on the decltype of the value.
Definition utility.hpp:29
constexpr std::optional< T > make_guideline(vertical_alignment alignment, T bottom, T top, T padding_bottom, T padding_top, T guideline_width)
Create a guideline between two points.
Definition alignment.hpp:61
axis
An enumeration of the 3 axis for 3D geometry.
Definition axis.hpp:18
@ middle
Align to the vertical-middle.
DOXYGEN BUG.
Definition algorithm.hpp:15
geometry/margins.hpp
Definition assert.hpp:18
constexpr value_type & width() noexcept
Access the x-as-width element from the extent.
Definition extent.hpp:167
constexpr value_type & height() noexcept
Access the y-as-height element from the extent.
Definition extent.hpp:178
2D constraints.
Definition box_constraints.hpp:25
Definition box_shape.hpp:15
Definition grid_layout.hpp:24
Definition grid_layout.hpp:228
constexpr reference operator[](size_t index) noexcept
Get element.
Definition grid_layout.hpp:540
constexpr size_t size() const noexcept
Number of cell on this axis.
Definition grid_layout.hpp:466
constexpr void layout(int new_position, int new_extent, std::optional< int > external_guideline, int guideline_width) noexcept
Layout each cell along an axis.
Definition grid_layout.hpp:406
constexpr const_reference back() const noexcept
Get the last element.
Definition grid_layout.hpp:596
constexpr std::tuple< int, int, int > constraints(cell_type const &cell) const noexcept
Get the minimum, preferred, maximum size of the span.
Definition grid_layout.hpp:360
constexpr bool empty() const noexcept
Check if this axis is empty.
Definition grid_layout.hpp:473
constexpr reference front() noexcept
Get the first element.
Definition grid_layout.hpp:563
constexpr reverse_iterator rend() noexcept
Iterator to the first cell on this axis.
Definition grid_layout.hpp:529
constexpr reverse_iterator rbegin() noexcept
Iterator to the first cell on this axis.
Definition grid_layout.hpp:522
constexpr reference back() noexcept
Get the last element.
Definition grid_layout.hpp:585
constexpr const_iterator end() const noexcept
Iterator to beyond the last cell on this axis.
Definition grid_layout.hpp:508
constexpr const_reference operator[](size_t index) const noexcept
Get element.
Definition grid_layout.hpp:552
constexpr const_iterator cbegin() const noexcept
Iterator to the first cell on this axis.
Definition grid_layout.hpp:494
constexpr iterator end() noexcept
Iterator to beyond the last cell on this axis.
Definition grid_layout.hpp:501
constexpr const_iterator begin() const noexcept
Iterator to the first cell on this axis.
Definition grid_layout.hpp:487
constexpr const_iterator cend() const noexcept
Iterator to beyond the last cell on this axis.
Definition grid_layout.hpp:515
constexpr iterator begin() noexcept
Iterator to the first cell on this axis.
Definition grid_layout.hpp:480
constexpr const_reference front() const noexcept
Get the first element.
Definition grid_layout.hpp:574
Definition grid_layout.hpp:237
int maximum
The maximum width/height of the cells.
Definition grid_layout.hpp:248
int margin_after
The right/bottom margin of the cells.
Definition grid_layout.hpp:256
bool beyond_maximum
Allow this cell to be resized beyond the maximum constraint.
Definition grid_layout.hpp:272
int extent
Size of the cell.
Definition grid_layout.hpp:284
int margin_before
The left/top margin of the cells.
Definition grid_layout.hpp:252
int minimum
The minimum width/height of the cells.
Definition grid_layout.hpp:240
int preferred
The preferred width/height of the cells.
Definition grid_layout.hpp:244
int padding_before
The left/top padding of the cells.
Definition grid_layout.hpp:260
alignment_type alignment
The alignment of the cells.
Definition grid_layout.hpp:268
int position
The position of the cell.
Definition grid_layout.hpp:278
std::optional< int > guideline
The before-position within this cell where to align to.
Definition grid_layout.hpp:290
int padding_after
The right/bottom padding of the cells.
Definition grid_layout.hpp:264
Grid layout algorithm.
Definition grid_layout.hpp:932
constexpr bool cell_in_use(size_t first_column, size_t first_row, size_t last_column, size_t last_row) noexcept
Check if the cell on the grid is already in use.
Definition grid_layout.hpp:1019
constexpr reference add_cell(size_t first_column, size_t first_row, size_t last_column, size_t last_row, Value &&value, bool beyond_maximum=false) noexcept
Check if the cell on the grid is already in use.
Definition grid_layout.hpp:1054
constexpr reference add_cell(size_t column, size_t row, Value &&value, bool beyond_maximum=false) noexcept
Check if the cell on the grid is already in use.
Definition grid_layout.hpp:1080
constexpr void set_layout(box_shape const &shape, int baseline_adjustment) noexcept
Layout the cells based on the width and height.
Definition grid_layout.hpp:1130
T emplace_back(T... args)