Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Axis refactor v13 - Preparation to multiple origo #629

Open
wants to merge 11 commits into
base: axis_refactor_v12d
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- Fix invalid read/write when animation is contiguous (onFinish callback calls setKeyframe).
- Waterfall chart preset not aligned.
- Split chart count negative values too.
- Split chart when same dimension on main and sub axis.

### Changed

Expand Down
213 changes: 213 additions & 0 deletions src/base/alg/union_foreach.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
#ifndef ALG_UNION_FOREACH_H
#define ALG_UNION_FOREACH_H

#include <cstdint>
#include <functional>
#include <ranges>

namespace Alg
{

namespace Impl
{
enum class union_call_t : std::uint8_t {
both,
only_left,
only_right
};

struct single_t
{
} constexpr static single{};
struct multi_t
{
} constexpr static multi{};

template <class F>
concept Property =
std::same_as<F, single_t> || std::same_as<F, multi_t>;

template <class F,
class I1,
class I2 = I1,
class Ptr1 = decltype(std::to_address(std::declval<I1 &>())),
class Ptr2 = decltype(std::to_address(std::declval<I2 &>()))>
concept address_binary_invocable =
std::copy_constructible<F>
&& (std::invocable<F &, Ptr1, Ptr2, union_call_t>
|| std::invocable<F &, Ptr1, Ptr2>);

template <class R>
concept insert_value_returns_iterator = requires {
{
std::declval<R &>().insert(
std::declval<const std::ranges::range_value_t<R> &>())
} -> std::same_as<std::ranges::iterator_t<R>>;
};

template <class R>
concept insert_value_returns_pair = requires {
{
std::declval<R &>().insert(
std::declval<const std::ranges::range_value_t<R> &>())
} -> std::same_as<std::pair<std::ranges::iterator_t<R>, bool>>;
};

template <std::ranges::forward_range R,
class CVR = std::remove_cvref_t<R>>
using default_property_t = typename std::conditional_t<
insert_value_returns_iterator<CVR>,
std::type_identity<multi_t>,
std::enable_if<insert_value_returns_pair<CVR>, single_t>>::type;

template <class Fun, class Arg1, class Arg2>
constexpr void
invoke(Fun &&f, Arg1 &&arg1, Arg2 &&arg2, union_call_t val)
{
if constexpr (std::invocable<Fun &&,
Arg1 &&,
Arg2 &&,
union_call_t>)
std::invoke(std::forward<Fun>(f),
std::forward<Arg1>(arg1),
std::forward<Arg2>(arg2),
val);
else
std::invoke(std::forward<Fun>(f),
std::forward<Arg1>(arg1),
std::forward<Arg2>(arg2));
}

template <class Exp, class Fun, class Arg>
constexpr const Arg &assignIfSame([[maybe_unused]] Arg &to,
const Arg &from)
{
if constexpr (std::same_as<Exp, Fun>)
return to = from;
else
return from;
}

}

using Impl::multi;
using Impl::single;
using Impl::union_call_t;

template <std::forward_iterator It1,
std::sentinel_for<It1> End1,
std::forward_iterator It2,
std::sentinel_for<It2> End2,
std::indirectly_unary_invocable<It1> Proj1 = std::identity,
std::indirectly_unary_invocable<It2> Proj2 = std::identity,
std::indirect_strict_weak_order<std::projected<It1, Proj1>,
std::projected<It2, Proj2>> Comp = std::ranges::less,
Impl::address_binary_invocable<It1, It2> Fun,
Impl::Property Prop>
constexpr void union_foreach(It1 first1,
End1 last1,
It2 first2,
End2 last2,
Fun f,
Prop,
Comp comp = {},
Proj1 proj1 = {},
Proj2 proj2 = {})
{
using enum union_call_t;
using Impl::assignIfSame;
using Impl::invoke;
const std::iter_value_t<It1> *lastValid1{};
const std::iter_value_t<It2> *lastValid2{};
while (first1 != last1 || first2 != last2)
if (first2 == last2
|| (first1 != last1
&& std::invoke(comp,
std::invoke(proj1, *first1),
std::invoke(proj2, *first2))))
invoke(f,
assignIfSame<Impl::single_t, Prop>(lastValid1,
std::to_address(first1++)),
lastValid2,
only_left);
else if (first1 == last1
|| std::invoke(comp,
std::invoke(proj2, *first2),
std::invoke(proj1, *first1)))
invoke(f,
lastValid1,
assignIfSame<Impl::single_t, Prop>(lastValid2,
std::to_address(first2++)),
only_right);
else {
auto &&key = std::invoke(proj1, *first1);
invoke(f,
lastValid1 = std::to_address(first1++),
lastValid2 = std::to_address(first2++),
both);

if constexpr (std::is_same_v<Impl::multi_t, Prop>) {
while (first1 != last1 && first2 != last2
&& !std::invoke(comp,
key,
std::invoke(proj1, *first1))
&& !std::invoke(comp,
key,
std::invoke(proj2, *first2)))
invoke(f,
lastValid1 = std::to_address(first1++),
lastValid2 = std::to_address(first2++),
both);

while (first1 != last1
&& !std::invoke(comp,
key,
std::invoke(proj1, *first1)))
invoke(f,
std::to_address(first1++),
lastValid2,
only_left);

while (first2 != last2
&& !std::invoke(comp,
key,
std::invoke(proj2, *first2)))
invoke(f,
lastValid1,
std::to_address(first2++),
only_right);

lastValid1 = nullptr;
lastValid2 = nullptr;
}
}
}

template <std::ranges::forward_range R,
std::indirectly_unary_invocable<std::ranges::iterator_t<R>> Proj =
std::identity,
std::indirect_strict_weak_order<
std::projected<std::ranges::iterator_t<R>, Proj>> Comp =
std::ranges::less,
Impl::address_binary_invocable<std::ranges::iterator_t<R>> Fun,
Impl::Property Prop = Impl::default_property_t<R>>
constexpr void union_foreach(const R &r1,
const R &r2,
Fun f,
Comp comp = {},
Proj proj = {},
Prop p = {})
{
union_foreach(std::ranges::begin(r1),
std::ranges::end(r1),
std::ranges::begin(r2),
std::ranges::end(r2),
std::move(f),
p,
std::move(comp),
proj,
proj);
}
}

#endif
19 changes: 11 additions & 8 deletions src/base/geom/affinetransform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,28 +70,31 @@ AffineTransform &AffineTransform::operator*=(
return *this;
}

Geom::Point AffineTransform::operator()(
const Geom::Point &original) const
Point AffineTransform::operator()(const Point &original) const
{
return {original.x * m[0][0] + original.y * m[0][1] + m[0][2],
original.x * m[1][0] + original.y * m[1][1] + m[1][2]};
}

Geom::Line AffineTransform::operator()(
const Geom::Line &original) const
Line AffineTransform::operator()(const Line &original) const
{
return {(*this)(original.begin), (*this)(original.end)};
}

Geom::Polygon AffineTransform::operator()(
const Geom::Polygon &original) const
Polygon AffineTransform::operator()(const Polygon &original) const
{
Geom::Polygon res;
Polygon res;
for (auto point : original.points) res.add((*this)(point));
return res;
}

void AffineTransform::shift(const Geom::Point &offset)
Size AffineTransform::operator()(const Size &original) const
{
return {original.x * m[0][0] + original.y * m[0][1],
original.x * m[1][0] + original.y * m[1][1]};
}

void AffineTransform::shift(const Point &offset)
{
m[0][2] += offset.x;
m[1][2] += offset.y;
Expand Down
11 changes: 6 additions & 5 deletions src/base/geom/affinetransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@ class AffineTransform
double m10,
double m11,
double m12);
explicit AffineTransform(Geom::Point offset,
explicit AffineTransform(Point offset,
double scale = 1.0,
double angle = 0.0);

[[nodiscard]] const Matrix &getMatrix() const { return m; }

[[nodiscard]] AffineTransform inverse() const;
[[nodiscard]] bool transforms() const;
void shift(const Geom::Point &offset);
void shift(const Point &offset);

friend AffineTransform operator*(AffineTransform lhs,
const AffineTransform &rhs)
Expand All @@ -46,9 +46,10 @@ class AffineTransform
AffineTransform &operator*=(const AffineTransform &other);
bool operator==(const AffineTransform &other) const = default;

Geom::Point operator()(const Geom::Point &original) const;
Geom::Line operator()(const Geom::Line &original) const;
Geom::Polygon operator()(const Geom::Polygon &original) const;
Point operator()(const Point &original) const;
Line operator()(const Line &original) const;
Polygon operator()(const Polygon &original) const;
Size operator()(const Size &original) const;

[[nodiscard]] std::string toJSON() const;

Expand Down
6 changes: 6 additions & 0 deletions src/base/geom/point.h
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,12 @@ struct Point

struct Size : Point
{
[[nodiscard]] static Size Oriented(Orientation orientation,
double value,
double other = 0.0)
{
return {Point::Coord(orientation, value, other)};
}

static Size Square(double size) { return {size, size}; }

Expand Down
19 changes: 0 additions & 19 deletions src/base/geom/rect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
#include <algorithm>
#include <array>
#include <cmath>
#include <compare>

#include "base/math/floating.h"

Expand Down Expand Up @@ -91,24 +90,6 @@ Rect Rect::intersection(const Rect &rect) const
Size{xRight - xLeft, yTop - yBottom}};
}

bool Rect::intersects(const Rect &r) const
{
using Math::Floating::is_zero;
using std::strong_order;
auto first = strong_order(right(), r.left());
auto second = strong_order(r.right(), left());
auto third = strong_order(top(), r.bottom());
auto fourth = strong_order(r.top(), bottom());

auto isOutside = is_lt(first) || is_lt(second) || is_lt(third)
|| is_lt(fourth)
|| ((is_eq(first) || is_eq(second))
&& !is_zero(width()) && !is_zero(r.width()))
|| ((is_eq(third) || is_eq(fourth))
&& !is_zero(height()) && !is_zero(r.height()));
return !isOutside;
}

Point Rect::center() const { return pos + size / 2.0; }

Rect Rect::popBottom(double length)
Expand Down
14 changes: 13 additions & 1 deletion src/base/geom/rect.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ struct Rect
return {bottom(), top()};
}

[[nodiscard]] Math::Range<> oSize(Orientation o) const
{
return isHorizontal(o) ? hSize() : vSize();
}

[[nodiscard]] Math::Range<> x() const
{
return {pos.x, pos.x + size.x};
Expand Down Expand Up @@ -101,6 +106,14 @@ struct Rect
setTop(range.max);
}

void setOSize(Orientation o, const Math::Range<> &range)
{
if (isHorizontal(o))
setHSize(range);
else
setVSize(range);
}

[[nodiscard]] Rect bottomHalf() const
{
return {pos, size.verticalHalf()};
Expand Down Expand Up @@ -138,7 +151,6 @@ struct Rect
[[nodiscard]] Rect intersection(const Rect &rect) const;

[[nodiscard]] bool contains(const Point &p) const;
[[nodiscard]] bool intersects(const Rect &r) const;
[[nodiscard]] Point center() const;

[[nodiscard]] Rect outline(const Geom::Size &margin) const
Expand Down
Loading