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);
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;
std::vector<std::unique_ptr<component>> release_children() override;
@ -47,8 +47,8 @@ namespace psemek::ui::impl
private:
react::value<float> margin_;
react::value<std::vector<react::value<box_layout::size_policy>>> size_policies_;
react::source<std::vector<react::value<size_polygon>>> children_size_constraints_;
react::value<size_polygon> size_constraints_;
react::source<std::vector<react::value<struct size_constraints>>> children_size_constraints_;
react::value<struct size_constraints> size_constraints_;
};
extern template struct box_layout_base<0>;

View file

@ -1,6 +1,6 @@
#pragma once
#include <psemek/ui/impl/size_polygon.hpp>
#include <psemek/ui/impl/size_constraints.hpp>
#include <psemek/react/value.hpp>
#include <psemek/geom/box.hpp>
#include <psemek/geom/interval.hpp>
@ -15,15 +15,12 @@ namespace psemek::ui::impl
{
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 geom::box<float, 2> const & shape() const;
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() {}

View file

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

View file

@ -13,15 +13,15 @@ namespace psemek::ui::impl
single_container_base(react::value<geom::vector<float, 2>> margin);
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;
std::unique_ptr<component> release_child() override;
private:
react::value<geom::vector<float, 2>> margin_;
react::source<react::value<size_polygon>> child_size_constraints_;
react::value<size_polygon> size_constraints_;
react::source<react::value<struct size_constraints>> child_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();
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;
std::vector<std::unique_ptr<component>> release_children() override;
@ -23,8 +23,8 @@ namespace psemek::ui::impl
{}
private:
react::source<std::vector<react::value<size_polygon>>> children_size_constraints_;
react::value<size_polygon> size_constraints_;
react::source<std::vector<react::value<struct size_constraints>>> children_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/size_polygon_utils.hpp>
#include <psemek/react/map.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{};
template <int Dimension>
size_polygon compute_size_constraints(std::vector<box_layout::size_policy> const & size_policies,
std::vector<size_polygon const *> const & children_size_constraints, float margin)
size_constraints compute_size_constraints(std::vector<box_layout::size_policy> const & size_policies,
std::vector<size_constraints> const & children_size_constraints, float margin)
{
size_polygon minimized{{0.f, 0.f}, {0.f, max_size}};
size_polygon weight_unit = minimized;
size_constraints minimized = size_constraints::max();
size_constraints weight_unit = minimized;
float weight_sum = 0.f;
for (std::size_t i = 0; i < size_policies.size(); ++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]))
{
size_polygon child = *children_size_constraints[i];
for (auto & p : child)
p[Dimension] /= weight->value;
weight_unit = sum_along(weight_unit, child, Dimension);
weight_unit = intersect(weight_unit, scale(children_size_constraints[i], 1.f / weight->value, Dimension));
weight_sum += weight->value;
}
}
@ -38,17 +34,15 @@ namespace psemek::ui::impl
auto result = std::move(minimized);
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)
p[Dimension] *= weight_sum;
result = sum_along(result, weight_unit, Dimension);
geom::vector shift_delta{0.f, 0.f};
shift_delta[Dimension] = margin * (size_policies.size() - 1);
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;
}
@ -68,7 +62,7 @@ namespace psemek::ui::impl
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];
}
else if (auto weight = std::get_if<box_layout::weight>(&policy))
@ -98,12 +92,16 @@ namespace psemek::ui::impl
: margin_(margin)
, size_policies_({})
, children_size_constraints_()
, size_constraints_(react::map([](auto const & size_policies, auto const & children_size_constraints, auto const & margin){
return compute_size_constraints<Dimension>(size_policies, children_size_constraints, margin);
},
react::join(react::map(react::unpack_with_default(default_policy), size_policies_)),
react::join(react::map(react::unpack_with_transform([](react::value<size_polygon> const & arg){ return &(*arg); }), children_size_constraints_)),
margin_))
, size_constraints_(
react::map(
[](auto const & size_policies, auto const & children_size_constraints, auto const & margin){
return compute_size_constraints<Dimension>(size_policies, 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>
@ -134,7 +132,7 @@ namespace psemek::ui::impl
}
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_;
}
@ -144,13 +142,13 @@ namespace psemek::ui::impl
{
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())
{
if (child)
children_size_constraints.push_back(child->size_constraints());
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));
}

View file

@ -3,24 +3,6 @@
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
{
return {};
@ -36,9 +18,10 @@ namespace psemek::ui::impl
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/size_polygon_utils.hpp>
#include <psemek/react/map.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)
: margin_(margin)
, child_size_constraints_(default_size_constraints())
, size_constraints_(react::map([](size_polygon const & child_constraints, geom::vector<float, 2> const & margin){
, child_size_constraints_(size_constraints::max())
, size_constraints_(react::map([](struct size_constraints const & child_constraints, geom::vector<float, 2> const & margin){
return shift(child_constraints, margin);
}, react::join(child_size_constraints_), margin))
{}
@ -22,7 +21,7 @@ namespace psemek::ui::impl
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_;
}
@ -30,13 +29,13 @@ namespace psemek::ui::impl
void single_container_base::set_child(std::unique_ptr<component> 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()
{
auto child = single_container::release_child();
child_size_constraints_.set(default_size_constraints());
child_size_constraints_.set(size_constraints::max());
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/size_polygon_utils.hpp>
#include <psemek/react/join.hpp>
#include <psemek/react/map.hpp>
@ -9,14 +8,11 @@ namespace psemek::ui::impl
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())
return component::default_size_polygon();
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]);
size_constraints result = size_constraints::max();
for (auto const & constraints : children_size_constraints)
result = intersect(result, constraints);
return result;
}
@ -25,7 +21,7 @@ namespace psemek::ui::impl
stack_layout_base::stack_layout_base()
: size_constraints_(react::map([](auto const & 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)
@ -35,7 +31,7 @@ namespace psemek::ui::impl
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_;
}
@ -43,13 +39,13 @@ namespace psemek::ui::impl
void stack_layout_base::set_children(std::vector<std::unique_ptr<component>> 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())
{
if (child)
children_size_constraints.push_back(child->size_constraints());
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));
}