Skip to content

Commit d0cdcf2

Browse files
authored
Merge pull request #631 from vizzuhq/axis_refactor_v13c
Axis refactor v13c - After multiple origo - bugfixes
2 parents 019786f + c71aaf3 commit d0cdcf2

File tree

20 files changed

+657
-530
lines changed

20 files changed

+657
-530
lines changed

CHANGELOG.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
- Fix invalid read/write when animation is contiguous (onFinish callback calls setKeyframe).
88
- Waterfall chart preset not aligned.
99
- Split chart count negative values too.
10-
- Split chart when same dimension on main and sub axis.
10+
- Split chart handled when same dimension on main and sub axis.
1111

1212
### Changed
1313

@@ -16,7 +16,6 @@
1616
- axis line multiplication.
1717
- axis labels multiplication.
1818
- axis range interpretation differently for all split part.
19-
- negative values are handled correctly.
2019
- align center / stretch fix.
2120

2221
### Added

src/base/alg/merge.h

Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
1+
#ifndef ALG_MERGE_H
2+
#define ALG_MERGE_H
3+
4+
#include <algorithm>
5+
#include <cstdint>
6+
#include <functional>
7+
#include <ranges>
8+
9+
namespace Alg
10+
{
11+
12+
// closest to std::ranges::merge, but:
13+
// - added new projection parameter to projection both ranges
14+
// - comparison is a three-way comparison which requires weak_order
15+
// on projected values
16+
// - added new transformer_1 and transformer_2 parameters to transform
17+
// the values at left and right side if no merging happens
18+
// - added need_merge parameter to decide if merging is necessary
19+
// of 2 equivalent values or just transforming all values
20+
// - added merger parameter to merge the values if merging happens
21+
// - output argument can be range which used as inserter_iterator
22+
// - arguments are passed as struct to avoid argument swapping
23+
// and used as named arguments
24+
25+
namespace Merge
26+
{
27+
struct get_first
28+
{
29+
template <class T, class... Args>
30+
constexpr auto operator()(T &&t, const Args &...) const noexcept
31+
{
32+
return std::forward<T>(t);
33+
}
34+
};
35+
36+
struct always
37+
{
38+
template <class... Ts>
39+
constexpr auto operator()(const Ts &...) const noexcept
40+
{
41+
return res;
42+
}
43+
bool res{true};
44+
};
45+
46+
template <typename T>
47+
concept comparison_category =
48+
!std::is_void_v<std::common_comparison_category_t<T>>;
49+
50+
template <typename T, typename Cat>
51+
concept compares_as =
52+
comparison_category<Cat>
53+
&& std::same_as<std::common_comparison_category_t<T, Cat>, Cat>;
54+
55+
template <typename R,
56+
typename T,
57+
typename U = T,
58+
typename Cat = std::weak_ordering>
59+
concept three_way_order =
60+
std::regular_invocable<R, T, U>
61+
&& compares_as<std::invoke_result_t<R, T, U>, Cat>;
62+
63+
template <typename R,
64+
typename Iter1,
65+
typename Iter2 = Iter1,
66+
typename Cat = std::weak_ordering>
67+
concept indirect_three_way_order =
68+
std::indirectly_readable<Iter1> && std::indirectly_readable<Iter2>
69+
&& std::copy_constructible<R>
70+
&& three_way_order<R &,
71+
std::iter_value_t<Iter1> &,
72+
std::iter_value_t<Iter2> &,
73+
Cat>
74+
&& three_way_order<R &,
75+
std::iter_value_t<Iter1> &,
76+
std::iter_reference_t<Iter2>,
77+
Cat>
78+
&& three_way_order<R &,
79+
std::iter_reference_t<Iter1>,
80+
std::iter_value_t<Iter2> &,
81+
Cat>
82+
&& three_way_order<R &,
83+
std::iter_reference_t<Iter1>,
84+
std::iter_reference_t<Iter2>,
85+
Cat>
86+
&& three_way_order<R &,
87+
std::iter_common_reference_t<Iter1>,
88+
std::iter_common_reference_t<Iter2>,
89+
Cat>;
90+
91+
template <typename... Ts>
92+
concept common_references =
93+
(std::common_reference_with<std::common_reference_t<Ts...>, Ts>
94+
&& ...);
95+
96+
template <typename R, typename Iter1, typename Iter2 = Iter1>
97+
concept indirectly_binary_invocable =
98+
std::indirectly_readable<Iter1> && std::indirectly_readable<Iter2>
99+
&& std::copy_constructible<R>
100+
&& std::invocable<R &,
101+
std::iter_value_t<Iter1> &,
102+
std::iter_value_t<Iter2> &>
103+
&& std::invocable<R &,
104+
std::iter_value_t<Iter1> &,
105+
std::iter_reference_t<Iter2>>
106+
&& std::invocable<R &,
107+
std::iter_reference_t<Iter1>,
108+
std::iter_value_t<Iter2> &>
109+
&& std::invocable<R &,
110+
std::iter_reference_t<Iter1>,
111+
std::iter_reference_t<Iter2>>
112+
&& std::invocable<R &,
113+
std::iter_common_reference_t<Iter1>,
114+
std::iter_common_reference_t<Iter2>>
115+
&& common_references<std::invoke_result_t<R &,
116+
std::iter_value_t<Iter1> &,
117+
std::iter_value_t<Iter2> &>,
118+
std::invoke_result_t<R &,
119+
std::iter_value_t<Iter1> &,
120+
std::iter_reference_t<Iter2>>,
121+
std::invoke_result_t<R &,
122+
std::iter_reference_t<Iter1>,
123+
std::iter_value_t<Iter2> &>,
124+
std::invoke_result_t<R &,
125+
std::iter_reference_t<Iter1>,
126+
std::iter_reference_t<Iter2>>>;
127+
128+
template <typename R, typename... Its>
129+
concept indirectly_unary_invocable_all =
130+
(std::indirectly_unary_invocable<R, Its> && ...);
131+
132+
template <class U, class T = std::remove_reference_t<U>>
133+
concept insertable_range_or_iterator =
134+
std::weakly_incrementable<std::remove_cv_t<T>>
135+
|| (std::ranges::range<U>
136+
&& std::weakly_incrementable<std::insert_iterator<T>>
137+
&& requires(U &cont,
138+
std::ranges::iterator_t<U> i,
139+
const std::ranges::range_value_t<U> &v) {
140+
std::inserter(cont, std::ranges::end(cont));
141+
i = cont.insert(i, v);
142+
++i;
143+
});
144+
145+
template <class T>
146+
struct borrowed_inserter_or_self :
147+
std::conditional<std::ranges::borrowed_range<T>,
148+
std::insert_iterator<std::remove_reference_t<T>>,
149+
std::ranges::dangling>
150+
{};
151+
152+
template <class T>
153+
requires(std::weakly_incrementable<std::remove_cvref_t<T>>)
154+
struct borrowed_inserter_or_self<T> :
155+
std::type_identity<std::remove_cvref_t<T>>
156+
{};
157+
158+
template <class T>
159+
using borrowed_inserter_or_self_t =
160+
typename borrowed_inserter_or_self<T>::type;
161+
162+
template <class Proj1 = std::identity,
163+
class Proj2 = std::identity,
164+
class Proj = std::identity,
165+
class Comp = decltype(std::weak_order),
166+
class Transform1 = std::identity,
167+
class Transform2 = std::identity,
168+
class NeedMerge = always,
169+
class Merge = get_first>
170+
struct merge_args
171+
{
172+
[[no_unique_address]] Proj1 projection_1{};
173+
[[no_unique_address]] Proj2 projection_2{};
174+
[[no_unique_address]] Proj projection{};
175+
[[no_unique_address]] Comp comparator{};
176+
[[no_unique_address]] Transform1 transformer_1{};
177+
[[no_unique_address]] Transform2 transformer_2{};
178+
[[no_unique_address]] NeedMerge need_merge{};
179+
[[no_unique_address]] Merge merger{};
180+
};
181+
182+
template <std::input_iterator It1,
183+
std::sentinel_for<It1> End1,
184+
std::input_iterator It2,
185+
std::sentinel_for<It2> End2,
186+
std::weakly_incrementable Out,
187+
std::indirectly_unary_invocable<It1> Proj1,
188+
std::indirectly_unary_invocable<It2> Proj2,
189+
indirectly_unary_invocable_all<std::projected<It1, Proj1>,
190+
std::projected<It2, Proj2>> Proj,
191+
indirect_three_way_order<
192+
std::projected<std::projected<It1, Proj1>, Proj>,
193+
std::projected<std::projected<It2, Proj2>, Proj>> Comp,
194+
std::indirectly_unary_invocable<It1> Transform1,
195+
std::indirectly_unary_invocable<It2> Transform2,
196+
std::indirect_binary_predicate<It1, It2> NeedMerge,
197+
indirectly_binary_invocable<It1, It2> Merge>
198+
constexpr std::ranges::in_in_out_result<It1, It2, Out> merge(
199+
It1 first1,
200+
End1 last1,
201+
It2 first2,
202+
End2 last2,
203+
Out result,
204+
merge_args<Proj1,
205+
Proj2,
206+
Proj,
207+
Comp,
208+
Transform1,
209+
Transform2,
210+
NeedMerge,
211+
Merge> &&args = {})
212+
{
213+
while (first1 != last1 && first2 != last2)
214+
if (auto cmp = std::invoke(args.comparator,
215+
std::invoke(args.projection,
216+
std::invoke(args.projection_1, *first1)),
217+
std::invoke(args.projection,
218+
std::invoke(args.projection_2, *first2)));
219+
is_eq(cmp)
220+
&& std::invoke(args.need_merge, *first1, *first2)) {
221+
*result = std::invoke(args.merger, *first1, *first2);
222+
++first1;
223+
++first2;
224+
++result;
225+
}
226+
else {
227+
if (is_lteq(cmp)) {
228+
*result = std::invoke(args.transformer_1, *first1);
229+
++first1;
230+
++result;
231+
}
232+
if (is_gteq(cmp)) {
233+
*result = std::invoke(args.transformer_2, *first2);
234+
++first2;
235+
++result;
236+
}
237+
}
238+
auto copy1 = std::ranges::transform(std::move(first1),
239+
std::move(last1),
240+
std::move(result),
241+
args.transformer_1);
242+
auto copy2 = std::ranges::transform(std::move(first2),
243+
std::move(last2),
244+
std::move(copy1.out),
245+
args.transformer_2);
246+
return {std::move(copy1.in),
247+
std::move(copy2.in),
248+
std::move(copy2.out)};
249+
}
250+
251+
template <std::ranges::input_range R1,
252+
std::ranges::input_range R2,
253+
insertable_range_or_iterator Out,
254+
class... Args>
255+
constexpr std::ranges::in_in_out_result<
256+
std::ranges::borrowed_iterator_t<R1>,
257+
std::ranges::borrowed_iterator_t<R2>,
258+
borrowed_inserter_or_self_t<Out>>
259+
merge(R1 &&range1,
260+
R2 &&range2,
261+
Out &&result,
262+
merge_args<Args...> &&args = {})
263+
{
264+
if constexpr (std::weakly_incrementable<
265+
std::remove_cvref_t<Out>>) {
266+
return ::Alg::Merge::merge(std::ranges::begin(range1),
267+
std::ranges::end(range1),
268+
std::ranges::begin(range2),
269+
std::ranges::end(range2),
270+
result,
271+
std::move(args));
272+
}
273+
else {
274+
return ::Alg::Merge::merge(std::ranges::begin(range1),
275+
std::ranges::end(range1),
276+
std::ranges::begin(range2),
277+
std::ranges::end(range2),
278+
std::inserter(result, std::ranges::end(result)),
279+
std::move(args));
280+
}
281+
}
282+
}
283+
284+
using Merge::merge;
285+
using Merge::merge_args;
286+
287+
}
288+
289+
#endif

0 commit comments

Comments
 (0)