layout.hpp 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. #ifndef LAYOUT_HPP
  2. #define LAYOUT_HPP
  3. #include "layout.h"
  4. template <typename Range>
  5. int2 select_corner(const Range& rng, int2 direction) noexcept
  6. {
  7. // center + direction * half_size
  8. return
  9. (
  10. (lower(rng) + upper(rng)) // center * 2
  11. + direction * (upper(rng) - lower(rng)) // direction * size
  12. ) / 2; // all halved
  13. }
  14. // support::deref here will deref a thing as much as possible, or not at all if not possible
  15. // this way we support any level of indirection in our range of objects that we can layout
  16. // now this does adjacent_difference, and I would love to use std, but many caveats...
  17. // first std::adjacent_difference very liberal with assuming value types, makes a temp stores intermediate values,
  18. // can't necessarily do that with move only heavy objects which is the whole reason why we can get indirection here,
  19. // but ok then deal with the indirection, the "pointers" themselves are value types and you can throw those around just fine...
  20. // 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
  21. // so yeah, adjacent difference just can't work here...
  22. // support::variance could work with magic iterator that would do support::deref under the hood, but I can't be bothered anymore,
  23. // 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
  24. template <typename ForwardItr>
  25. constexpr void layout_bounds(ForwardItr begin, ForwardItr end, const int2& spacing)
  26. {
  27. assert(begin != end);
  28. const auto mask = int2(spacing != int2::zero());
  29. const auto corner = signum(spacing);
  30. for(auto prev = begin++; begin != end; ++begin, ++prev)
  31. {
  32. support::deref(begin) +=
  33. (
  34. select_corner(support::deref(prev), corner)
  35. + spacing
  36. - select_corner(support::deref(begin), -corner)
  37. ) * mask;
  38. }
  39. }
  40. template <typename Range>
  41. bounds_layout<Range>::bounds_layout(Range elements, int2 spacing) :
  42. movable_bounds(invalid_range),
  43. elements(std::move(elements)),
  44. spacing(spacing)
  45. { }
  46. template <typename Range>
  47. bounds_layout<Range>::bounds_layout(int2 spacing) :
  48. movable_bounds(invalid_range),
  49. elements{},
  50. spacing(spacing)
  51. { }
  52. template <typename Range>
  53. auto bounds_layout<Range>::operator+=(const int2& offset)
  54. -> bounds_layout&
  55. {
  56. movable_bounds::operator+=(offset);
  57. for(auto&& element : elements)
  58. support::deref(element) += offset;
  59. return *this;
  60. }
  61. template <typename Range>
  62. range2D bounds_layout<Range>::update()
  63. {
  64. using std::begin;
  65. using std::end;
  66. layout_bounds(begin(elements), end(elements), spacing);
  67. using ::lower;
  68. using ::upper;
  69. bounds = invalid_range; // invalid range, returned for empty layout
  70. for(auto&& element : elements)
  71. {
  72. bounds.lower() = min(bounds.lower(), lower(support::deref(element)));
  73. bounds.upper() = max(bounds.upper(), upper(support::deref(element)));
  74. }
  75. return bounds;
  76. }
  77. #endif /* end of include guard */