Replace ui::size_polygon with simpler 2d box

This commit is contained in:
Nikita Lisitsa 2024-07-21 14:32:57 +03:00
parent 325dc01757
commit 018f3ae0b0
14 changed files with 144 additions and 251 deletions

View file

@ -37,7 +37,7 @@ namespace psemek::ui::impl
box_layout_base(react::value<float> margin); box_layout_base(react::value<float> margin);
void reshape(geom::box<float, 2> const & new_shape) override; void reshape(geom::box<float, 2> const & new_shape) override;
react::value<size_polygon> size_constraints() const override; react::value<struct size_constraints> size_constraints() const override;
void set_children(std::vector<std::unique_ptr<component>> children) override; void set_children(std::vector<std::unique_ptr<component>> children) override;
std::vector<std::unique_ptr<component>> release_children() override; std::vector<std::unique_ptr<component>> release_children() override;
@ -47,8 +47,8 @@ namespace psemek::ui::impl
private: private:
react::value<float> margin_; react::value<float> margin_;
react::value<std::vector<react::value<box_layout::size_policy>>> size_policies_; react::value<std::vector<react::value<box_layout::size_policy>>> size_policies_;
react::source<std::vector<react::value<size_polygon>>> children_size_constraints_; react::source<std::vector<react::value<struct size_constraints>>> children_size_constraints_;
react::value<size_polygon> size_constraints_; react::value<struct size_constraints> size_constraints_;
}; };
extern template struct box_layout_base<0>; extern template struct box_layout_base<0>;

View file

@ -1,6 +1,6 @@
#pragma once #pragma once
#include <psemek/ui/impl/size_polygon.hpp> #include <psemek/ui/impl/size_constraints.hpp>
#include <psemek/react/value.hpp> #include <psemek/react/value.hpp>
#include <psemek/geom/box.hpp> #include <psemek/geom/box.hpp>
#include <psemek/geom/interval.hpp> #include <psemek/geom/interval.hpp>
@ -15,15 +15,12 @@ namespace psemek::ui::impl
{ {
static constexpr float infinity = std::numeric_limits<float>::infinity(); static constexpr float infinity = std::numeric_limits<float>::infinity();
static size_polygon const & default_size_polygon();
static react::value<size_polygon> default_size_constraints();
virtual util::span<std::unique_ptr<component> const> children() const; virtual util::span<std::unique_ptr<component> const> children() const;
virtual geom::box<float, 2> const & shape() const; virtual geom::box<float, 2> const & shape() const;
virtual void reshape(geom::box<float, 2> const & new_shape); virtual void reshape(geom::box<float, 2> const & new_shape);
virtual react::value<size_polygon> size_constraints() const; virtual react::value<struct size_constraints> size_constraints() const;
virtual ~component() {} virtual ~component() {}

View file

@ -4,8 +4,6 @@
#include <psemek/ui/key.hpp> #include <psemek/ui/key.hpp>
#include <psemek/util/signal.hpp> #include <psemek/util/signal.hpp>
#include <any>
namespace psemek::ui::impl namespace psemek::ui::impl
{ {

View file

@ -13,15 +13,15 @@ namespace psemek::ui::impl
single_container_base(react::value<geom::vector<float, 2>> margin); single_container_base(react::value<geom::vector<float, 2>> margin);
void reshape(geom::box<float, 2> const & new_shape) override; void reshape(geom::box<float, 2> const & new_shape) override;
react::value<size_polygon> size_constraints() const override; react::value<struct size_constraints> size_constraints() const override;
void set_child(std::unique_ptr<component> child) override; void set_child(std::unique_ptr<component> child) override;
std::unique_ptr<component> release_child() override; std::unique_ptr<component> release_child() override;
private: private:
react::value<geom::vector<float, 2>> margin_; react::value<geom::vector<float, 2>> margin_;
react::source<react::value<size_polygon>> child_size_constraints_; react::source<react::value<struct size_constraints>> child_size_constraints_;
react::value<size_polygon> size_constraints_; react::value<struct size_constraints> size_constraints_;
}; };
} }

View file

@ -0,0 +1,28 @@
#pragma once
#include <psemek/geom/box.hpp>
namespace psemek::ui::impl
{
struct size_constraints
{
geom::box<float, 2> box;
static size_constraints max();
};
size_constraints shift(size_constraints const & constraints, geom::vector<float, 2> const & delta);
size_constraints scale(size_constraints const & constraints, float factor, int dimension);
float min(size_constraints const & constraints, int dimension);
size_constraints intersect(size_constraints const & constraints1, size_constraints const & constraints2);
geom::interval<float> range(size_constraints const & constraints, int dimension);
size_constraints cut(size_constraints const & constraints, int dimension, geom::interval<float> const & range);
size_constraints sum_along(size_constraints const & constraints1, size_constraints const & constraints2, int dimension);
}

View file

@ -1,15 +0,0 @@
#pragma once
#include <psemek/geom/point.hpp>
#include <vector>
namespace psemek::ui::impl
{
constexpr float max_size = 16384.f;
// Convex, in CCW order
using size_polygon = std::vector<geom::point<float, 2>>;
}

View file

@ -1,19 +0,0 @@
#pragma once
#include <psemek/ui/impl/size_polygon.hpp>
#include <psemek/geom/interval.hpp>
namespace psemek::ui::impl
{
size_polygon shift(size_polygon polygon, geom::vector<float, 2> const & delta);
size_polygon min(size_polygon const & polygon, int dimension);
size_polygon intersect(size_polygon const & polygon1, size_polygon const & polygon2);
geom::interval<float> range(size_polygon const & polygon, int dimension);
size_polygon cut(size_polygon const & polygon, int dimension, geom::interval<float> const & range);
size_polygon sum_along(size_polygon const & polygon1, size_polygon const & polygon2, int dimension);
}

View file

@ -14,7 +14,7 @@ namespace psemek::ui::impl
stack_layout_base(); stack_layout_base();
void reshape(geom::box<float, 2> const & new_shape) override; void reshape(geom::box<float, 2> const & new_shape) override;
react::value<size_polygon> size_constraints() const override; react::value<struct size_constraints> size_constraints() const override;
void set_children(std::vector<std::unique_ptr<component>> children) override; void set_children(std::vector<std::unique_ptr<component>> children) override;
std::vector<std::unique_ptr<component>> release_children() override; std::vector<std::unique_ptr<component>> release_children() override;
@ -23,8 +23,8 @@ namespace psemek::ui::impl
{} {}
private: private:
react::source<std::vector<react::value<size_polygon>>> children_size_constraints_; react::source<std::vector<react::value<struct size_constraints>>> children_size_constraints_;
react::value<size_polygon> size_constraints_; react::value<struct size_constraints> size_constraints_;
}; };
} }

View file

@ -1,5 +1,4 @@
#include <psemek/ui/impl/box_layout_base.hpp> #include <psemek/ui/impl/box_layout_base.hpp>
#include <psemek/ui/impl/size_polygon_utils.hpp>
#include <psemek/react/map.hpp> #include <psemek/react/map.hpp>
#include <psemek/react/join.hpp> #include <psemek/react/join.hpp>
@ -12,25 +11,22 @@ namespace psemek::ui::impl
static constexpr box_layout::size_policy default_policy = box_layout::weight{}; static constexpr box_layout::size_policy default_policy = box_layout::weight{};
template <int Dimension> template <int Dimension>
size_polygon compute_size_constraints(std::vector<box_layout::size_policy> const & size_policies, size_constraints compute_size_constraints(std::vector<box_layout::size_policy> const & size_policies,
std::vector<size_polygon const *> const & children_size_constraints, float margin) std::vector<size_constraints> const & children_size_constraints, float margin)
{ {
size_polygon minimized{{0.f, 0.f}, {0.f, max_size}}; size_constraints minimized = size_constraints::max();
size_polygon weight_unit = minimized; size_constraints weight_unit = minimized;
float weight_sum = 0.f; float weight_sum = 0.f;
for (std::size_t i = 0; i < size_policies.size(); ++i) for (std::size_t i = 0; i < size_policies.size(); ++i)
{ {
if (std::get_if<box_layout::minimized>(&size_policies[i])) if (std::get_if<box_layout::minimized>(&size_policies[i]))
{ {
minimized = sum_along(minimized, *children_size_constraints[i], Dimension); minimized = sum_along(minimized, children_size_constraints[i], Dimension);
} }
else if (auto weight = std::get_if<box_layout::weight>(&size_policies[i])) else if (auto weight = std::get_if<box_layout::weight>(&size_policies[i]))
{ {
size_polygon child = *children_size_constraints[i]; weight_unit = intersect(weight_unit, scale(children_size_constraints[i], 1.f / weight->value, Dimension));
for (auto & p : child)
p[Dimension] /= weight->value;
weight_unit = sum_along(weight_unit, child, Dimension);
weight_sum += weight->value; weight_sum += weight->value;
} }
} }
@ -38,17 +34,15 @@ namespace psemek::ui::impl
auto result = std::move(minimized); auto result = std::move(minimized);
if (weight_sum > 0.f) if (weight_sum > 0.f)
result = sum_along(result, scale(weight_unit, weight_sum, Dimension), Dimension);
if (size_policies.size() > 0)
{ {
for (auto & p : weight_unit) geom::vector shift_delta{0.f, 0.f};
p[Dimension] *= weight_sum; shift_delta[Dimension] = margin * (size_policies.size() - 1);
result = sum_along(result, weight_unit, Dimension); result = shift(std::move(result), shift_delta);
} }
geom::vector shift_delta{0.f, 0.f};
shift_delta[Dimension] = margin;
result = shift(std::move(result), shift_delta);
return result; return result;
} }
@ -68,7 +62,7 @@ namespace psemek::ui::impl
if (std::get_if<box_layout::minimized>(&policy)) if (std::get_if<box_layout::minimized>(&policy))
{ {
result[i] = min(*children[i]->size_constraints(), Dimension).front()[Dimension]; result[i] = min(*children[i]->size_constraints(), Dimension);
total_size -= result[i]; total_size -= result[i];
} }
else if (auto weight = std::get_if<box_layout::weight>(&policy)) else if (auto weight = std::get_if<box_layout::weight>(&policy))
@ -98,12 +92,16 @@ namespace psemek::ui::impl
: margin_(margin) : margin_(margin)
, size_policies_({}) , size_policies_({})
, children_size_constraints_() , children_size_constraints_()
, size_constraints_(react::map([](auto const & size_policies, auto const & children_size_constraints, auto const & margin){ , size_constraints_(
return compute_size_constraints<Dimension>(size_policies, children_size_constraints, margin); react::map(
}, [](auto const & size_policies, auto const & children_size_constraints, auto const & margin){
react::join(react::map(react::unpack_with_default(default_policy), size_policies_)), return compute_size_constraints<Dimension>(size_policies, children_size_constraints, margin);
react::join(react::map(react::unpack_with_transform([](react::value<size_polygon> const & arg){ return &(*arg); }), children_size_constraints_)), },
margin_)) react::join(react::map(react::unpack_with_default(default_policy), size_policies_)),
react::join(react::map(react::unpack, children_size_constraints_)),
margin_
)
)
{} {}
template <int Dimension> template <int Dimension>
@ -134,7 +132,7 @@ namespace psemek::ui::impl
} }
template <int Dimension> template <int Dimension>
react::value<size_polygon> box_layout_base<Dimension>::size_constraints() const react::value<size_constraints> box_layout_base<Dimension>::size_constraints() const
{ {
return size_constraints_; return size_constraints_;
} }
@ -144,13 +142,13 @@ namespace psemek::ui::impl
{ {
container::set_children(std::move(children)); container::set_children(std::move(children));
std::vector<react::value<size_polygon>> children_size_constraints; std::vector<react::value<struct size_constraints>> children_size_constraints;
for (auto const & child : this->children()) for (auto const & child : this->children())
{ {
if (child) if (child)
children_size_constraints.push_back(child->size_constraints()); children_size_constraints.push_back(child->size_constraints());
else else
children_size_constraints.push_back(default_size_constraints()); children_size_constraints.push_back(size_constraints::max());
} }
children_size_constraints_.set(std::move(children_size_constraints)); children_size_constraints_.set(std::move(children_size_constraints));
} }

View file

@ -3,24 +3,6 @@
namespace psemek::ui::impl namespace psemek::ui::impl
{ {
size_polygon const & component::default_size_polygon()
{
static size_polygon const result{{
{max_size, 0.f},
{0.f, 0.f},
{0.f, max_size},
{max_size, max_size}
}};
return result;
}
react::value<size_polygon> component::default_size_constraints()
{
static react::value<size_polygon> const result{default_size_polygon()};
return result;
}
util::span<std::unique_ptr<component> const> component::children() const util::span<std::unique_ptr<component> const> component::children() const
{ {
return {}; return {};
@ -36,9 +18,10 @@ namespace psemek::ui::impl
shape_ = new_shape; shape_ = new_shape;
} }
react::value<size_polygon> component::size_constraints() const react::value<size_constraints> component::size_constraints() const
{ {
return default_size_constraints(); static react::value<struct size_constraints> const result{size_constraints::max()};
return result;
} }
} }

View file

@ -1,5 +1,4 @@
#include <psemek/ui/impl/single_container_base.hpp> #include <psemek/ui/impl/single_container_base.hpp>
#include <psemek/ui/impl/size_polygon_utils.hpp>
#include <psemek/react/map.hpp> #include <psemek/react/map.hpp>
#include <psemek/react/join.hpp> #include <psemek/react/join.hpp>
@ -8,8 +7,8 @@ namespace psemek::ui::impl
single_container_base::single_container_base(react::value<geom::vector<float, 2>> margin) single_container_base::single_container_base(react::value<geom::vector<float, 2>> margin)
: margin_(margin) : margin_(margin)
, child_size_constraints_(default_size_constraints()) , child_size_constraints_(size_constraints::max())
, size_constraints_(react::map([](size_polygon const & child_constraints, geom::vector<float, 2> const & margin){ , size_constraints_(react::map([](struct size_constraints const & child_constraints, geom::vector<float, 2> const & margin){
return shift(child_constraints, margin); return shift(child_constraints, margin);
}, react::join(child_size_constraints_), margin)) }, react::join(child_size_constraints_), margin))
{} {}
@ -22,7 +21,7 @@ namespace psemek::ui::impl
child()->reshape(geom::shrink(new_shape, *margin_)); child()->reshape(geom::shrink(new_shape, *margin_));
} }
react::value<size_polygon> single_container_base::size_constraints() const react::value<size_constraints> single_container_base::size_constraints() const
{ {
return size_constraints_; return size_constraints_;
} }
@ -30,13 +29,13 @@ namespace psemek::ui::impl
void single_container_base::set_child(std::unique_ptr<component> child) void single_container_base::set_child(std::unique_ptr<component> child)
{ {
single_container::set_child(std::move(child)); single_container::set_child(std::move(child));
child_size_constraints_.set(this->child() ? this->child()->size_constraints() : default_size_constraints()); child_size_constraints_.set(this->child() ? this->child()->size_constraints() : size_constraints::max());
} }
std::unique_ptr<component> single_container_base::release_child() std::unique_ptr<component> single_container_base::release_child()
{ {
auto child = single_container::release_child(); auto child = single_container::release_child();
child_size_constraints_.set(default_size_constraints()); child_size_constraints_.set(size_constraints::max());
return child; return child;
} }

View file

@ -0,0 +1,63 @@
#include <psemek/ui/impl/size_constraints.hpp>
#include <limits>
namespace psemek::ui::impl
{
size_constraints size_constraints::max()
{
static constexpr float infinity = std::numeric_limits<float>::infinity();
return
{{{
{0.f, infinity},
{0.f, infinity},
}}};
}
size_constraints shift(size_constraints const & constraints, geom::vector<float, 2> const & delta)
{
return {constraints.box + delta};
}
size_constraints scale(size_constraints const & constraints, float factor, int dimension)
{
auto result = constraints;
result.box[dimension].min *= factor;
result.box[dimension].max *= factor;
return result;
}
float min(size_constraints const & constraints, int dimension)
{
return constraints.box[dimension].min;
}
size_constraints intersect(size_constraints const & constraints1, size_constraints const & constraints2)
{
return {constraints1.box & constraints2.box};
}
geom::interval<float> range(size_constraints const & constraints, int dimension)
{
return constraints.box[dimension];
}
size_constraints cut(size_constraints const & constraints, int dimension, geom::interval<float> const & range)
{
size_constraints result = constraints;
result.box[dimension] &= range;
return result;
}
size_constraints sum_along(size_constraints const & constraints1, size_constraints const & constraints2, int dimension)
{
size_constraints result;
result.box[dimension].min = constraints1.box[dimension].min + constraints2.box[dimension].min;
result.box[dimension].max = constraints1.box[dimension].max + constraints2.box[dimension].max;
result.box[dimension ^ 1] = constraints1.box[dimension ^ 1] & constraints2.box[dimension ^ 1];
return result;
}
}

View file

@ -1,135 +0,0 @@
#include <psemek/ui/impl/size_polygon_utils.hpp>
#include <psemek/geom/math.hpp>
#include <psemek/util/not_implemented.hpp>
#include <psemek/util/cyclic_iterator.hpp>
namespace psemek::ui::impl
{
size_polygon shift(size_polygon polygon, geom::vector<float, 2> const & delta)
{
for (auto & p : polygon)
p += delta;
return polygon;
}
size_polygon min(size_polygon const & polygon, int dimension)
{
if (polygon.empty())
return {};
size_polygon result;
auto cit = util::make_cyclic_iterator(polygon);
std::size_t iterations = 0;
for (auto next = std::next(cit); (*next)[dimension] <= (*cit)[dimension] && iterations < polygon.size(); cit = next++, ++iterations);
if (iterations == polygon.size())
return polygon;
for (auto prev = std::prev(cit); (*prev)[dimension] <= (*cit)[dimension]; cit = prev--);
for (auto cjt = cit; (*cjt)[dimension] == (*cit)[dimension]; ++cjt)
result.push_back(*cjt);
return result;
}
size_polygon intersect(size_polygon const & polygon1, size_polygon const & polygon2)
{
// TODO: https://www.cs.jhu.edu/~misha/Spring16/ORourke82.pdf
(void)polygon1;
(void)polygon2;
util::not_implemented();
}
geom::interval<float> range(size_polygon const & polygon, int dimension)
{
geom::interval<float> result;
for (auto const & p : polygon)
result |= p[dimension];
return result;
}
size_polygon cut(size_polygon const & polygon, int dimension, geom::interval<float> const & range)
{
size_polygon result;
std::size_t iterations = 0;
for (auto it = util::make_cyclic_iterator(polygon), next_it = std::next(it); iterations != polygon.size(); (it = next_it++), ++iterations)
{
auto const & curr = *it;
auto const & next = *next_it;
auto intersection = [&](float value)
{
// curr + (next - curr) * t = value
float t = (value - curr[dimension]) / (next[dimension] - curr[dimension]);
geom::point<float, 2> p;
p[dimension] = value;
p[dimension ^ 1] = geom::lerp(curr[dimension ^ 1], next[dimension ^ 1], t);
return p;
};
auto classify = [&](float value)
{
if (value < range.min)
return -2;
else if (value == range.min)
return -1;
else if (value < range.max)
return 0;
else if (value == range.max)
return 1;
else // (value > range.max)
return 2;
};
int const curr_class = classify(curr[dimension]);
int const next_class = classify(next[dimension]);
if (curr_class >= -1 && curr_class <= 1)
result.push_back(curr);
bool const min = (curr_class == -2 && next_class >= 0) || (curr_class >= 0 && next_class == -2);
bool const max = (curr_class == 2 && next_class <= 0) || (curr_class <= 0 && next_class == 2);
if (range.min == range.max)
{
if (min) result.push_back(intersection(range.min));
}
else
{
if (min) result.push_back(intersection(range.min));
if (max) result.push_back(intersection(range.max));
if (min && max && curr_class == 2)
std::swap(result.back(), result[result.size() - 2]);
}
}
return result;
}
size_polygon sum_along(size_polygon const & polygon1, size_polygon const & polygon2, int dimension)
{
if (polygon1.empty() || polygon2.empty())
return size_polygon{};
int const other_dimension = dimension ^ 1;
auto range1 = range(polygon1, other_dimension);
auto range2 = range(polygon2, other_dimension);
auto common_range = range1 & range2;
if (common_range.empty())
return size_polygon{};
auto cut1 = cut(polygon1, other_dimension, common_range);
auto cut2 = cut(polygon2, other_dimension, common_range);
util::not_implemented();
}
}

View file

@ -1,5 +1,4 @@
#include <psemek/ui/impl/stack_layout_base.hpp> #include <psemek/ui/impl/stack_layout_base.hpp>
#include <psemek/ui/impl/size_polygon_utils.hpp>
#include <psemek/react/join.hpp> #include <psemek/react/join.hpp>
#include <psemek/react/map.hpp> #include <psemek/react/map.hpp>
@ -9,14 +8,11 @@ namespace psemek::ui::impl
namespace namespace
{ {
size_polygon compute_size_constraints(std::vector<size_polygon const *> const & children_size_constraints) size_constraints compute_size_constraints(std::vector<size_constraints> const & children_size_constraints)
{ {
if (children_size_constraints.empty()) size_constraints result = size_constraints::max();
return component::default_size_polygon(); for (auto const & constraints : children_size_constraints)
result = intersect(result, constraints);
auto result = *children_size_constraints.front();
for (std::size_t i = 1; i < children_size_constraints.size(); ++i)
result = intersect(result, *children_size_constraints[i]);
return result; return result;
} }
@ -25,7 +21,7 @@ namespace psemek::ui::impl
stack_layout_base::stack_layout_base() stack_layout_base::stack_layout_base()
: size_constraints_(react::map([](auto const & children_size_constraints){ : size_constraints_(react::map([](auto const & children_size_constraints){
return compute_size_constraints(children_size_constraints); return compute_size_constraints(children_size_constraints);
}, react::join(react::map(react::unpack_with_transform([](react::value<size_polygon> const & arg){ return &(*arg); }), children_size_constraints_)))) }, react::join(react::map(react::unpack, children_size_constraints_))))
{} {}
void stack_layout_base::reshape(geom::box<float, 2> const & new_shape) void stack_layout_base::reshape(geom::box<float, 2> const & new_shape)
@ -35,7 +31,7 @@ namespace psemek::ui::impl
child->reshape(new_shape); child->reshape(new_shape);
} }
react::value<size_polygon> stack_layout_base::size_constraints() const react::value<size_constraints> stack_layout_base::size_constraints() const
{ {
return size_constraints_; return size_constraints_;
} }
@ -43,13 +39,13 @@ namespace psemek::ui::impl
void stack_layout_base::set_children(std::vector<std::unique_ptr<component>> children) void stack_layout_base::set_children(std::vector<std::unique_ptr<component>> children)
{ {
container::set_children(std::move(children)); container::set_children(std::move(children));
std::vector<react::value<size_polygon>> children_size_constraints; std::vector<react::value<struct size_constraints>> children_size_constraints;
for (auto const & child : this->children()) for (auto const & child : this->children())
{ {
if (child) if (child)
children_size_constraints.push_back(child->size_constraints()); children_size_constraints.push_back(child->size_constraints());
else else
children_size_constraints.push_back(default_size_constraints()); children_size_constraints.push_back(size_constraints::max());
} }
children_size_constraints_.set(std::move(children_size_constraints)); children_size_constraints_.set(std::move(children_size_constraints));
} }