12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788 |
- #ifndef LAYOUT_HPP
- #define LAYOUT_HPP
- #include "layout.h"
- template <typename Range>
- int2 select_corner(const Range& rng, int2 direction) noexcept
- {
- // center + direction * half_size
- return
- (
- (lower(rng) + upper(rng)) // center * 2
- + direction * (upper(rng) - lower(rng)) // direction * size
- ) / 2; // all halved
- }
- // support::deref here will deref a thing as much as possible, or not at all if not possible
- // this way we support any level of indirection in our range of objects that we can layout
- // now this does adjacent_difference, and I would love to use std, but many caveats...
- // first std::adjacent_difference very liberal with assuming value types, makes a temp stores intermediate values,
- // can't necessarily do that with move only heavy objects which is the whole reason why we can get indirection here,
- // but ok then deal with the indirection, the "pointers" themselves are value types and you can throw those around just fine...
- // yeah but the pointers are const... cause I don't want you changing the pointers on me I want you to just modify the pointees
- // so yeah, adjacent difference just can't work here...
- // support::variance could work with magic iterator that would do support::deref under the hood, but I can't be bothered anymore,
- // will do that if I find one more use case for it, otherwise better to just try to steer clear of heavy objects and indirection
- template <typename ForwardItr>
- constexpr void layout_bounds(ForwardItr begin, ForwardItr end, const int2& spacing)
- {
- assert(begin != end);
- const auto mask = int2(spacing != int2::zero());
- const auto corner = signum(spacing);
- for(auto prev = begin++; begin != end; ++begin, ++prev)
- {
- support::deref(begin) +=
- (
- select_corner(support::deref(prev), corner)
- + spacing
- - select_corner(support::deref(begin), -corner)
- ) * mask;
- }
- }
- template <typename Range>
- bounds_layout<Range>::bounds_layout(Range elements, int2 spacing) :
- movable_bounds(invalid_range),
- elements(std::move(elements)),
- spacing(spacing)
- { }
- template <typename Range>
- bounds_layout<Range>::bounds_layout(int2 spacing) :
- movable_bounds(invalid_range),
- elements{},
- spacing(spacing)
- { }
- template <typename Range>
- auto bounds_layout<Range>::operator+=(const int2& offset)
- -> bounds_layout&
- {
- movable_bounds::operator+=(offset);
- for(auto&& element : elements)
- support::deref(element) += offset;
- return *this;
- }
- template <typename Range>
- range2D bounds_layout<Range>::update()
- {
- using std::begin;
- using std::end;
- layout_bounds(begin(elements), end(elements), spacing);
- using ::lower;
- using ::upper;
- bounds = invalid_range; // invalid range, returned for empty layout
- for(auto&& element : elements)
- {
- bounds.lower() = min(bounds.lower(), lower(support::deref(element)));
- bounds.upper() = max(bounds.upper(), upper(support::deref(element)));
- }
- return bounds;
- }
- #endif /* end of include guard */
|