From 6085cd790c219108cd6416c182de1f38b229f029 Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Tue, 3 Dec 2024 11:56:52 +0100 Subject: [PATCH 01/11] add union-foreach --- src/base/alg/union_foreach.h | 200 +++++++++++++++++++++++++++++++ src/base/math/segmentedfunc.h | 43 ++++--- src/chart/generator/axis.cpp | 73 +++++------ src/dataframe/impl/dataframe.cpp | 64 ++++------ 4 files changed, 272 insertions(+), 108 deletions(-) create mode 100644 src/base/alg/union_foreach.h diff --git a/src/base/alg/union_foreach.h b/src/base/alg/union_foreach.h new file mode 100644 index 000000000..6885d241b --- /dev/null +++ b/src/base/alg/union_foreach.h @@ -0,0 +1,200 @@ +#ifndef ALG_UNION_FOREACH_H +#define ALG_UNION_FOREACH_H + +#include + +#include "union_foreach.h" + +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 +concept Property = + std::same_as || std::same_as; + +template ())), + class Ptr2 = decltype(std::to_address(std::declval()))> +concept address_binary_invocable = + std::copy_constructible + && (std::invocable + || std::invocable); + +template +concept insert_value_returns_iterator = requires { + { + std::declval().insert( + std::declval &>()) + } -> std::same_as>; +}; + +template +concept insert_value_returns_pair = requires { + { + std::declval().insert( + std::declval &>()) + } -> std::same_as, bool>>; +}; + +template > +using default_property_t = typename std::conditional_t< + insert_value_returns_iterator, + std::type_identity, + std::enable_if, single_t>>::type; + +template +constexpr void +invoke(Fun &&f, Arg1 &&arg1, Arg2 &&arg2, union_call_t val) +{ + if constexpr (std::invocable) + std::invoke(std::forward(f), + std::forward(arg1), + std::forward(arg2), + val); + else + std::invoke(std::forward(f), + std::forward(arg1), + std::forward(arg2)); +} +} + +using Impl::multi; +using Impl::single; +using Impl::union_call_t; + +template End1, + std::forward_iterator It2, + std::sentinel_for End2, + std::indirectly_unary_invocable Proj1 = std::identity, + std::indirectly_unary_invocable Proj2 = std::identity, + std::indirect_strict_weak_order, + std::projected> Comp = std::ranges::less, + Impl::address_binary_invocable 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::invoke; + constexpr static std::add_pointer_t> + value1_nullptr{nullptr}; + constexpr static std::add_pointer_t> + value2_nullptr{nullptr}; + while (first1 != last1 || first2 != last2) + if (first2 == last2 + || (first1 != last1 + && std::invoke(comp, + std::invoke(proj1, *first1), + std::invoke(proj2, *first2)))) + invoke(f, + std::to_address(first1++), + value2_nullptr, + only_left); + else if (first1 == last1 + || std::invoke(comp, + std::invoke(proj2, *first2), + std::invoke(proj1, *first1))) + invoke(f, + value1_nullptr, + std::to_address(first2++), + only_right); + else { + auto last1SameKey = std::to_address(first1); + auto last2SameKey = std::to_address(first2); + auto &&key = std::invoke(proj1, *first1); + invoke(f, + std::to_address(first1++), + std::to_address(first2++), + both); + + if constexpr (std::is_same_v) { + while (first1 != last1 && first2 != last2 + && !std::invoke(comp, + key, + std::invoke(proj1, *first1)) + && !std::invoke(comp, + key, + std::invoke(proj2, *first2))) + invoke(f, + last1SameKey = std::to_address(first1++), + last2SameKey = std::to_address(first2++), + both); + + while (first1 != last1 + && !std::invoke(comp, + key, + std::invoke(proj1, *first1))) + invoke(f, + std::to_address(first1++), + last2SameKey, + only_left); + + while (first2 != last2 + && !std::invoke(comp, + key, + std::invoke(proj2, *first2))) + invoke(f, + last1SameKey, + std::to_address(first2++), + only_right); + } + } +} + +template > Proj = + std::identity, + std::indirect_strict_weak_order< + std::projected, Proj>> Comp = + std::ranges::less, + Impl::address_binary_invocable> Fun, + Impl::Property Prop = Impl::default_property_t> +constexpr void union_foreach(R &&r1, + 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 diff --git a/src/base/math/segmentedfunc.h b/src/base/math/segmentedfunc.h index 8e04235b8..ec77fafd6 100644 --- a/src/base/math/segmentedfunc.h +++ b/src/base/math/segmentedfunc.h @@ -4,6 +4,8 @@ #include #include +#include "base/alg/union_foreach.h" + #include "interpolation.h" #include "range.h" @@ -40,28 +42,25 @@ template struct SegmentedFunction const CRTP &other) { CRTP res; - auto &stops = self.stops; - - for (auto it0 = stops.begin(), it1 = other.stops.begin(); - it0 != stops.end() || it1 != other.stops.end();) { - if (it1 == other.stops.end() - || (it0 != stops.end() && it0->pos < it1->pos)) { - res.stops.emplace_back(it0->pos, - it0->value + other(it0->pos)); - ++it0; - } - else if (it0 == stops.end() || it1->pos < it0->pos) { - res.stops.emplace_back(it1->pos, - it1->value + self(it1->pos)); - ++it1; - } - else { - res.stops.emplace_back(it0->pos, - it0->value + it1->value); - ++it1; - ++it0; - } - } + + Alg::union_foreach( + self.stops, + other.stops, + [&](const Stop *lhs, const Stop *rhs) + { + if (!rhs) + res.stops.emplace_back(lhs->pos, + lhs->value + other(lhs->pos)); + else if (!lhs) + res.stops.emplace_back(rhs->pos, + rhs->value + self(rhs->pos)); + else + res.stops.emplace_back(lhs->pos, + lhs->value + rhs->value); + }, + {}, + &Stop::pos, + Alg::single); return res; } diff --git a/src/chart/generator/axis.cpp b/src/chart/generator/axis.cpp index 151d0a552..506618d38 100644 --- a/src/chart/generator/axis.cpp +++ b/src/chart/generator/axis.cpp @@ -12,6 +12,7 @@ #include #include +#include "base/alg/union_foreach.h" #include "base/anim/interpolated.h" #include "base/geom/point.h" #include "base/math/floating.h" @@ -295,52 +296,32 @@ DimensionAxis interpolate(const DimensionAxis &op0, DimensionAxis res; res.factor = factor; - - for (auto first1 = op0.values.begin(), - first2 = op1.values.begin(), - last1 = op0.values.end(), - last2 = op1.values.end(); - first1 != last1 || first2 != last2;) - if (first2 == last2 - || (first1 != last1 && first1->first < first2->first)) { - res.values.emplace(std::piecewise_construct, - std::tuple{first1->first}, - std::forward_as_tuple(first1->second, true)); - ++first1; - } - else if (first1 == last1 || first2->first < first1->first) { - res.values.emplace(std::piecewise_construct, - std::tuple{first2->first}, - std::forward_as_tuple(first2->second, false)); - ++first2; - } - else { - auto key = first1->first; - auto to1 = op0.values.upper_bound(key); - auto to2 = op1.values.upper_bound(key); - - while (first1 != to1 && first2 != to2) - res.values.emplace(key, - interpolate(first1++->second, - first2++->second, - factor)); - - for (const auto &latest = std::prev(to2)->second; - first1 != to1; - ++first1) - res.values - .emplace(key, - interpolate(first1->second, latest, factor)) - ->second.endPos.makeAuto(); - - for (const auto &latest = std::prev(to1)->second; - first2 != to2; - ++first2) - res.values - .emplace(key, - interpolate(latest, first2->second, factor)) - ->second.startPos.makeAuto(); - } + using Ptr = DimensionAxis::Values::const_pointer; + Alg::union_foreach( + op0.values, + op1.values, + [&res](Ptr v1, Ptr v2, Alg::union_call_t type) + { + if (!v2) + res.values.emplace(std::piecewise_construct, + std::tuple{v1->first}, + std::forward_as_tuple(v1->second, true)); + else if (!v1) + res.values.emplace(std::piecewise_construct, + std::tuple{v2->first}, + std::forward_as_tuple(v2->second, false)); + else if (auto &&val = res.values + .emplace(v1->first, + interpolate(v1->second, + v2->second, + res.factor)) + ->second; + type == Alg::union_call_t::only_left) + val.endPos.makeAuto(); + else if (type == Alg::union_call_t::only_right) + val.startPos.makeAuto(); + }, + res.values.value_comp()); return res; } diff --git a/src/dataframe/impl/dataframe.cpp b/src/dataframe/impl/dataframe.cpp index feece15d4..48c95178b 100644 --- a/src/dataframe/impl/dataframe.cpp +++ b/src/dataframe/impl/dataframe.cpp @@ -29,54 +29,38 @@ namespace Vizzu::dataframe { using Refl::unsafe_get; +template +[[nodiscard]] std::shared_ptr create_interface( + Ts &&...ts) +{ + auto &&iptr = std::make_unique(); + auto &&ptr = std::unique_ptr{ + new (iptr->data) dataframe(std::forward(ts)...), + std::destroy_at}; + return {iptr.release(), + [newly = std::move(ptr), deleter = iptr.get_deleter()]( + dataframe_interface *df) mutable + { + newly.reset(); + deleter(df); + }}; +} + std::shared_ptr dataframe::copy( bool inherit_sorting) const & { - auto &&uptr = std::make_unique(); - void *&&data = uptr->data; - auto &&my_deleter = [data](dataframe *df) - { - df->~dataframe(); - ::operator delete(data, df); - }; - using ptr_t = std::unique_ptr>; - const auto *&&cp = get_if(&source); - return {uptr.release(), - [newly = ptr_t{new (data) dataframe( - cp ? cp->other - : unsafe_get( - source), - cp && cp->pre_remove ? &*cp->pre_remove - : nullptr, - cp && inherit_sorting && cp->sorted_indices - ? &*cp->sorted_indices - : nullptr), - std::move(my_deleter)}](dataframe_interface *df) mutable - { - newly.reset(); - std::default_delete{}(df); - }}; + return create_interface( + cp ? cp->other : unsafe_get(source), + cp && cp->pre_remove ? &*cp->pre_remove : nullptr, + cp && inherit_sorting && cp->sorted_indices + ? &*cp->sorted_indices + : nullptr); } std::shared_ptr dataframe::create_new() { - auto &&uptr = std::make_unique(); - auto my_deleter = [p = uptr.get()](dataframe *df) - { - df->~dataframe(); - ::operator delete(p, df); - }; - auto &&uptr2 = std::unique_ptr{ - new (uptr->data) dataframe(), - std::move(my_deleter)}; - return {uptr.release(), - [rm = std::move(uptr2)](dataframe_interface *df) mutable - { - rm.reset(); - std::default_delete{}(df); - }}; + return create_interface(); } dataframe::dataframe(std::shared_ptr other, From 23928ac0c07ae0be4f8f502ae449fb110b5d02ab Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Wed, 4 Dec 2024 15:53:27 +0100 Subject: [PATCH 02/11] started to implement multiaxis --- src/base/alg/union_foreach.h | 45 +++++---- src/base/geom/affinetransform.cpp | 19 ++-- src/base/geom/affinetransform.h | 11 ++- src/base/geom/point.h | 5 + src/base/math/segmentedfunc.h | 16 +++- src/chart/generator/axis.cpp | 88 ++++++++++++++++++ src/chart/generator/axis.h | 28 +++++- src/chart/generator/plotbuilder.cpp | 44 +++++---- src/chart/generator/plotbuilder.h | 12 ++- src/chart/rendering/drawaxes.cpp | 119 +++++++++++++++++------- src/chart/rendering/drawaxes.h | 16 +++- src/chart/rendering/drawguides.cpp | 16 ++-- src/chart/rendering/drawguides.h | 5 +- src/chart/rendering/drawinterlacing.cpp | 46 +++++---- src/chart/rendering/drawinterlacing.h | 12 ++- src/chart/rendering/drawplot.cpp | 2 +- 16 files changed, 361 insertions(+), 123 deletions(-) diff --git a/src/base/alg/union_foreach.h b/src/base/alg/union_foreach.h index 6885d241b..375b9e036 100644 --- a/src/base/alg/union_foreach.h +++ b/src/base/alg/union_foreach.h @@ -77,6 +77,17 @@ invoke(Fun &&f, Arg1 &&arg1, Arg2 &&arg2, union_call_t val) std::forward(arg1), std::forward(arg2)); } + +template +constexpr const Arg &assignIfSame([[maybe_unused]] Arg &to, + const Arg &from) +{ + if constexpr (std::same_as) + return to = from; + else + return from; +} + } using Impl::multi; @@ -104,11 +115,10 @@ constexpr void union_foreach(It1 first1, Proj2 proj2 = {}) { using enum union_call_t; + using Impl::assignIfSame; using Impl::invoke; - constexpr static std::add_pointer_t> - value1_nullptr{nullptr}; - constexpr static std::add_pointer_t> - value2_nullptr{nullptr}; + const std::iter_value_t *lastValid1{}; + const std::iter_value_t *lastValid2{}; while (first1 != last1 || first2 != last2) if (first2 == last2 || (first1 != last1 @@ -116,24 +126,24 @@ constexpr void union_foreach(It1 first1, std::invoke(proj1, *first1), std::invoke(proj2, *first2)))) invoke(f, - std::to_address(first1++), - value2_nullptr, + assignIfSame(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, - value1_nullptr, - std::to_address(first2++), + lastValid1, + assignIfSame(lastValid2, + std::to_address(first2++)), only_right); else { - auto last1SameKey = std::to_address(first1); - auto last2SameKey = std::to_address(first2); auto &&key = std::invoke(proj1, *first1); invoke(f, - std::to_address(first1++), - std::to_address(first2++), + lastValid1 = std::to_address(first1++), + lastValid2 = std::to_address(first2++), both); if constexpr (std::is_same_v) { @@ -145,8 +155,8 @@ constexpr void union_foreach(It1 first1, key, std::invoke(proj2, *first2))) invoke(f, - last1SameKey = std::to_address(first1++), - last2SameKey = std::to_address(first2++), + lastValid1 = std::to_address(first1++), + lastValid2 = std::to_address(first2++), both); while (first1 != last1 @@ -155,7 +165,7 @@ constexpr void union_foreach(It1 first1, std::invoke(proj1, *first1))) invoke(f, std::to_address(first1++), - last2SameKey, + lastValid2, only_left); while (first2 != last2 @@ -163,9 +173,12 @@ constexpr void union_foreach(It1 first1, key, std::invoke(proj2, *first2))) invoke(f, - last1SameKey, + lastValid1, std::to_address(first2++), only_right); + + lastValid1 = nullptr; + lastValid2 = nullptr; } } } diff --git a/src/base/geom/affinetransform.cpp b/src/base/geom/affinetransform.cpp index efb204d48..4c218588e 100644 --- a/src/base/geom/affinetransform.cpp +++ b/src/base/geom/affinetransform.cpp @@ -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; diff --git a/src/base/geom/affinetransform.h b/src/base/geom/affinetransform.h index f8f332a36..67ae40013 100644 --- a/src/base/geom/affinetransform.h +++ b/src/base/geom/affinetransform.h @@ -28,7 +28,7 @@ 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); @@ -36,7 +36,7 @@ class AffineTransform [[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) @@ -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; diff --git a/src/base/geom/point.h b/src/base/geom/point.h index 8fcd87b63..18d906554 100644 --- a/src/base/geom/point.h +++ b/src/base/geom/point.h @@ -236,6 +236,11 @@ struct Point struct Size : Point { + [[nodiscard]] static Size + Coord(Orientation orientation, double value, double other = 0.0) + { + return {Point::Coord(orientation, value, other)}; + } static Size Square(double size) { return {size, size}; } diff --git a/src/base/math/segmentedfunc.h b/src/base/math/segmentedfunc.h index ec77fafd6..4b4519db1 100644 --- a/src/base/math/segmentedfunc.h +++ b/src/base/math/segmentedfunc.h @@ -46,17 +46,25 @@ template struct SegmentedFunction Alg::union_foreach( self.stops, other.stops, - [&](const Stop *lhs, const Stop *rhs) + [&](const Stop *lhs, + const Stop *rhs, + Alg::union_call_t type) { - if (!rhs) + switch (type) { + case Alg::union_call_t::only_left: res.stops.emplace_back(lhs->pos, lhs->value + other(lhs->pos)); - else if (!lhs) + break; + case Alg::union_call_t::only_right: res.stops.emplace_back(rhs->pos, rhs->value + self(rhs->pos)); - else + break; + case Alg::union_call_t::both: + default: res.stops.emplace_back(lhs->pos, lhs->value + rhs->value); + break; + } }, {}, &Stop::pos, diff --git a/src/chart/generator/axis.cpp b/src/chart/generator/axis.cpp index 506618d38..2d5310b11 100644 --- a/src/chart/generator/axis.cpp +++ b/src/chart/generator/axis.cpp @@ -340,4 +340,92 @@ DimensionAxis::Item interpolate(const DimensionAxis::Item &op0, return res; } +SplitAxis +interpolate(const SplitAxis &op0, const SplitAxis &op1, double factor) +{ + using Math::Niebloid::interpolate; + SplitAxis res; + static_cast(res) = + interpolate(static_cast(op0), + static_cast(op1), + factor); + if (!op0.parts.empty() && !op1.parts.empty()) { + using PartPair = const decltype(res.parts)::value_type; + Alg::union_foreach( + op0.parts, + op1.parts, + [&res, &factor](PartPair *lhs, + PartPair *rhs, + Alg::union_call_t type) + { + switch (type) { + case Alg::union_call_t::only_left: { + auto from = lhs->second.range.getMin(); + res.parts[lhs->first] = { + .weight = interpolate(lhs->second.weight, + 0.0, + factor), + .range = interpolate(lhs->second.range, + Math::Range<>::Raw(from, from), + factor)}; + break; + } + case Alg::union_call_t::only_right: { + auto from = rhs->second.range.getMin(); + res.parts[rhs->first] = { + .weight = interpolate(0.0, + rhs->second.weight, + factor), + .range = interpolate( + Math::Range<>::Raw(from, from), + rhs->second.range, + factor)}; + break; + } + default: + case Alg::union_call_t::both: { + res.parts[lhs->first] = + interpolate(lhs->second, rhs->second, factor); + break; + } + } + }, + res.parts.value_comp()); + } + else if (!op0.parts.empty()) { + auto begin = op0.parts.begin(); + res.parts[begin->first] = { + .weight = interpolate(begin->second.weight, 1.0, factor), + .range = interpolate(begin->second.range, + Math::Range<>::Raw(0, 1), + factor)}; + while (++begin != op0.parts.end()) { + res.parts[begin->first] = { + .weight = + interpolate(begin->second.weight, 0.0, factor), + .range = interpolate(begin->second.range, + Math::Range<>::Raw(0, 1), + factor)}; + } + } + else if (!op1.parts.empty()) { + auto begin = op1.parts.begin(); + res.parts[begin->first] = { + .weight = interpolate(1.0, begin->second.weight, factor), + .range = interpolate(Math::Range<>::Raw(0, 1), + begin->second.range, + factor)}; + while (++begin != op1.parts.end()) { + res.parts[begin->first] = { + .weight = + interpolate(0.0, begin->second.weight, factor), + .range = interpolate(Math::Range<>::Raw(0, 1), + begin->second.range, + factor)}; + } + } + + return res; +} + } diff --git a/src/chart/generator/axis.h b/src/chart/generator/axis.h index a982833de..bddd0b414 100644 --- a/src/chart/generator/axis.h +++ b/src/chart/generator/axis.h @@ -196,6 +196,28 @@ struct Axis [[nodiscard]] bool operator==(const Axis &other) const = default; }; +struct SplitAxis : Axis +{ + struct Part + { + double weight{1.0}; + Math::Range<> range = Math::Range<>::Raw(0, 1); + + [[nodiscard]] bool operator==( + const Part &other) const = default; + }; + + using Parts = std::map; + Parts parts; + + [[nodiscard]] bool operator==( + const SplitAxis &other) const = default; + + friend SplitAxis interpolate(const SplitAxis &op0, + const SplitAxis &op1, + double factor); +}; + struct Axises { struct CalculatedLegend @@ -206,7 +228,7 @@ struct Axises }; std::array, 2> leftLegend; - Refl::EnumArray axises; + Refl::EnumArray axises; struct Label { ::Anim::String unit; @@ -236,12 +258,12 @@ struct Axises return leftLegend[0].emplace(legendType).calc; } - [[nodiscard]] const Axis &at(AxisId axisType) const + [[nodiscard]] const SplitAxis &at(AxisId axisType) const { return axises[axisType]; } - [[nodiscard]] Axis &at(AxisId axisType) + [[nodiscard]] SplitAxis &at(AxisId axisType) { return axises[axisType]; } diff --git a/src/chart/generator/plotbuilder.cpp b/src/chart/generator/plotbuilder.cpp index e55cb09aa..6b6326b41 100644 --- a/src/chart/generator/plotbuilder.cpp +++ b/src/chart/generator/plotbuilder.cpp @@ -71,11 +71,11 @@ void PlotBuilder::addAxisLayout(Buckets &buckets, const std::size_t &subBucketSize, const Data::DataTable &dataTable) { - linkMarkers(buckets, mainBucketSize, subBucketSize); - calcAxises(dataTable); - addAlignment(buckets, plot->getOptions()->subAxisType()); - addAlignment(buckets.sort(&Marker::mainId), - plot->getOptions()->mainAxisType()); + linkMarkers(buckets); + calcAxises(dataTable, buckets, mainBucketSize, subBucketSize); + addAlignment(buckets, plot->getOptions()->mainAxisType()); + addAlignment(buckets.sort(&Marker::subId), + plot->getOptions()->subAxisType()); } void PlotBuilder::initDimensionTrackers() @@ -187,21 +187,13 @@ void PlotBuilder::addSpecLayout(Buckets &buckets) } } -void PlotBuilder::linkMarkers(Buckets &buckets, - const std::size_t &mainBucketSize, - const std::size_t &subBucketSize) +void PlotBuilder::linkMarkers(Buckets &buckets) { auto &&hasMarkerConnection = linkMarkers(buckets.sort(&Marker::mainId), plot->getOptions()->mainAxisType()); - addSeparation(buckets, - plot->getOptions()->mainAxisType(), - subBucketSize); std::ignore = linkMarkers(buckets.sort(&Marker::subId), plot->getOptions()->subAxisType()); - addSeparation(buckets, - plot->getOptions()->subAxisType(), - mainBucketSize); if (hasMarkerConnection && plot->getOptions()->geometry.get() == ShapeType::line @@ -311,8 +303,20 @@ bool PlotBuilder::linkMarkers(const Buckets &buckets, return hasConnection; } -void PlotBuilder::calcAxises(const Data::DataTable &dataTable) +void PlotBuilder::calcAxises(const Data::DataTable &dataTable, + Buckets &buckets, + const std::size_t &mainBucketSize, + const std::size_t &subBucketSize) { + auto &&[subRanges, subMax] = addSeparation(buckets, + plot->getOptions()->subAxisType(), + mainBucketSize); + + auto &&[mainRanges, mainMax] = + addSeparation(buckets.sort(&Marker::mainId), + plot->getOptions()->mainAxisType(), + subBucketSize); + const auto &xrange = plot->getOptions()->getHorizontalAxis().range; const auto &yrange = plot->getOptions()->getVerticalAxis().range; @@ -524,11 +528,12 @@ void PlotBuilder::addAlignment(const Buckets &buckets, } } -void PlotBuilder::addSeparation(const Buckets &buckets, +std::pair>, Math::Range<>> +PlotBuilder::addSeparation(const Buckets &buckets, AxisId axisIndex, const std::size_t &otherBucketSize) const { - if (!plot->getOptions()->isSplit(axisIndex)) return; + if (!plot->getOptions()->isSplit(axisIndex)) return {}; const auto &axisProps = plot->getOptions()->getChannels().axisPropsAt(axisIndex); @@ -551,7 +556,8 @@ void PlotBuilder::addSeparation(const Buckets &buckets, if (anyEnabled[i]) max = max + ranges[i]; auto splitSpace = - plot->getStyle().plot.getAxis(axisIndex).spacing->get(max.max, + plot->getStyle().plot.getAxis(axisIndex).spacing->get( + max.size(), plot->getStyle().calculatedSize()); for (auto i = 1U; i < ranges.size(); ++i) @@ -566,6 +572,8 @@ void PlotBuilder::addSeparation(const Buckets &buckets, Base::Align{align, ranges[i]}.getAligned( marker.getSizeBy(axisIndex))); } + + return {ranges, max}; } void PlotBuilder::normalizeSizes() diff --git a/src/chart/generator/plotbuilder.h b/src/chart/generator/plotbuilder.h index b96d7af67..34cc490a6 100644 --- a/src/chart/generator/plotbuilder.h +++ b/src/chart/generator/plotbuilder.h @@ -36,16 +36,18 @@ class PlotBuilder void initDimensionTrackers(); Buckets generateMarkers(std::size_t &mainBucketSize, std::size_t &subBucketSize); - void linkMarkers(Buckets &buckets, - const std::size_t &mainBucketSize, - const std::size_t &subBucketSize); + void linkMarkers(Buckets &buckets); [[nodiscard]] bool linkMarkers(const Buckets &buckets, AxisId axisIndex) const; - void calcAxises(const Data::DataTable &dataTable); + void calcAxises(const Data::DataTable &dataTable, + Buckets &buckets, + const std::size_t &mainBucketSize, + const std::size_t &subBucketSize); void calcLegendAndLabel(const Data::DataTable &dataTable); void calcAxis(const Data::DataTable &dataTable, AxisId type); void addAlignment(const Buckets &buckets, AxisId axisIndex) const; - void addSeparation(const Buckets &buckets, + [[nodiscard]] std::pair>, Math::Range<>> + addSeparation(const Buckets &buckets, AxisId axisIndex, const std::size_t &otherBucketSize) const; void normalizeSizes(); diff --git a/src/chart/rendering/drawaxes.cpp b/src/chart/rendering/drawaxes.cpp index 6efef787f..c138cb8f0 100644 --- a/src/chart/rendering/drawaxes.cpp +++ b/src/chart/rendering/drawaxes.cpp @@ -39,32 +39,74 @@ namespace Vizzu::Draw void DrawAxes::drawGeometries() const { - DrawInterlacing{*this}.drawGeometries(Gen::AxisId::y); - DrawInterlacing{*this}.drawGeometries(Gen::AxisId::x); - - drawAxis(Gen::AxisId::x); - drawAxis(Gen::AxisId::y); + for (auto &xSplit : std::views::values(splits[Gen::AxisId::x])) + for (auto &ySplit : + std::views::values(splits[Gen::AxisId::y])) { + double weight = + Math::FuzzyBool::And(xSplit.weight, ySplit.weight); + if (Math::Floating::is_zero(weight)) continue; - DrawGuides{*this}.draw(Gen::AxisId::x); - DrawGuides{*this}.draw(Gen::AxisId::y); + Geom::AffineTransform tr{xSplit.range.size(), + 0.0, + xSplit.range.getMin(), + 0.0, + ySplit.range.size(), + ySplit.range.getMin()}; + + DrawInterlacing{*this}.drawGeometries(Gen::AxisId::y, + tr, + weight); + DrawInterlacing{*this}.drawGeometries(Gen::AxisId::x, + tr, + weight); + + drawAxis(Gen::AxisId::x, tr, weight); + drawAxis(Gen::AxisId::y, tr, weight); + + DrawGuides{*this}.draw(Gen::AxisId::x, tr, weight); + DrawGuides{*this}.draw(Gen::AxisId::y, tr, weight); + } } void DrawAxes::drawLabels() const { - DrawInterlacing{*this}.drawTexts(Gen::AxisId::y); - DrawInterlacing{*this}.drawTexts(Gen::AxisId::x); - - drawDimensionLabels(Gen::AxisId::x); - drawDimensionLabels(Gen::AxisId::y); + for (auto &xSplit : std::views::values(splits[Gen::AxisId::x])) + for (auto &ySplit : + std::views::values(splits[Gen::AxisId::y])) { + double weight = + Math::FuzzyBool::And(xSplit.weight, ySplit.weight); + if (Math::Floating::is_zero(weight)) continue; - drawTitle(Gen::AxisId::x); - drawTitle(Gen::AxisId::y); + Geom::AffineTransform tr{xSplit.range.size(), + 0.0, + xSplit.range.getMin(), + 0.0, + ySplit.range.size(), + ySplit.range.getMin()}; + + DrawInterlacing{*this}.drawTexts(Gen::AxisId::y, + tr, + weight); + DrawInterlacing{*this}.drawTexts(Gen::AxisId::x, + tr, + weight); + + drawDimensionLabels(Gen::AxisId::x, tr, weight); + drawDimensionLabels(Gen::AxisId::y, tr, weight); + + drawTitle(Gen::AxisId::x, tr, weight); + drawTitle(Gen::AxisId::y, tr, weight); + } } const DrawAxes &&DrawAxes::init() && { for (auto axisIndex : Refl::enum_values()) { - const auto &axis = getAxis(axisIndex); + const auto &axis = plot->axises.at(axisIndex); + + const static Gen::SplitAxis::Parts oneSized{{}}; + splits[axisIndex] = + axis.parts.empty() ? oneSized : axis.parts; auto measEnabled = axis.measure.enabled.combine(); auto &intervals = this->intervals[axisIndex]; @@ -229,12 +271,14 @@ Geom::Line DrawAxes::getAxisLine(Gen::AxisId axisIndex) const return {}; } -void DrawAxes::drawAxis(Gen::AxisId axisIndex) const +void DrawAxes::drawAxis(Gen::AxisId axisIndex, + const Geom::AffineTransform &tr, + double w) const { - if (auto line = getAxisLine(axisIndex); !line.isPoint()) { - auto lineColor = - *rootStyle.plot.getAxis(axisIndex).color - * static_cast(plot->guides.at(axisIndex).axis); + if (auto line = tr(getAxisLine(axisIndex)); !line.isPoint()) { + auto lineColor = *rootStyle.plot.getAxis(axisIndex).color + * Math::FuzzyBool::And(w, + plot->guides.at(axisIndex).axis); if (lineColor.isTransparent()) return; @@ -331,7 +375,9 @@ Geom::Point DrawAxes::getTitleOffset(Gen::AxisId axisIndex, : Geom::Point{orthogonal, -parallel}; } -void DrawAxes::drawTitle(Gen::AxisId axisIndex) const +void DrawAxes::drawTitle(Gen::AxisId axisIndex, + const Geom::AffineTransform &tr, + double w) const { const auto &titleString = getAxis(axisIndex).title; @@ -345,7 +391,8 @@ void DrawAxes::drawTitle(Gen::AxisId axisIndex) const auto title = titleString.get_or_first(index); if (title.value.empty()) continue; - auto weight = Math::FuzzyBool::And(title.weight, + auto weight = Math::FuzzyBool::And(w, + title.weight, titleStyle.position->get_or_first(index).weight, titleStyle.vposition->get_or_first(index).weight); @@ -363,7 +410,7 @@ void DrawAxes::drawTitle(Gen::AxisId axisIndex) const getTitleOffset(axisIndex, index, fades == ::Anim::second); auto posDir = coordSys.convertDirectionAt( - {relCenter, relCenter + normal}); + tr(Geom::Line{relCenter, relCenter + normal})); auto posAngle = posDir.getDirection().angle(); @@ -416,7 +463,9 @@ void DrawAxes::drawTitle(Gen::AxisId axisIndex) const } } -void DrawAxes::drawDimensionLabels(Gen::AxisId axisIndex) const +void DrawAxes::drawDimensionLabels(Gen::AxisId axisIndex, + const Geom::AffineTransform &tr, + double w) const { const auto &labelStyle = rootStyle.plot.getAxis(axisIndex).label; @@ -435,7 +484,9 @@ void DrawAxes::drawDimensionLabels(Gen::AxisId axisIndex) const drawDimensionLabel(axisIndex, origo, interval, - Math::FuzzyBool::And(interval.weight, + tr, + Math::FuzzyBool::And(w, + interval.weight, enabled.labels)); } } @@ -444,6 +495,7 @@ void DrawAxes::drawDimensionLabels(Gen::AxisId axisIndex) const void DrawAxes::drawDimensionLabel(Gen::AxisId axisIndex, const Geom::Point &origo, const Interval &interval, + const Geom::AffineTransform &tr, double weight) const { if (weight == 0) return; @@ -454,6 +506,7 @@ void DrawAxes::drawDimensionLabel(Gen::AxisId axisIndex, auto drawLabel = OrientedLabel{{ctx()}}; labelStyle.position->visit( [this, + &tr, &axisIndex, &drawLabel, &labelStyle, @@ -489,14 +542,14 @@ void DrawAxes::drawDimensionLabel(Gen::AxisId axisIndex, : labelStyle.side->factor( Styles::AxisLabel::Side::negative); - auto draw = - [&, - posDir = coordSys - .convertDirectionAt( - {relCenter, relCenter + normal}) - .extend(1 - 2 * under)]( - const ::Anim::Weighted &str, - double plusWeight = 1.0) + auto draw = [&, + posDir = coordSys + .convertDirectionAt( + tr(Geom::Line{relCenter, + relCenter + normal})) + .extend(1 - 2 * under)]( + const ::Anim::Weighted &str, + double plusWeight = 1.0) { if (!str.value) return; drawLabel.draw(canvas, diff --git a/src/chart/rendering/drawaxes.h b/src/chart/rendering/drawaxes.h index 4afdfc69f..0696df994 100644 --- a/src/chart/rendering/drawaxes.h +++ b/src/chart/rendering/drawaxes.h @@ -64,6 +64,9 @@ class DrawAxes : public DrawingContext Refl::EnumArray> intervals; Refl::EnumArray> separators; + Refl::EnumArray> + splits; [[nodiscard]] const auto &getIntervals( Gen::AxisId axisIndex) const @@ -87,12 +90,19 @@ class DrawAxes : public DrawingContext [[nodiscard]] Geom::Point getTitleOffset(Gen::AxisId axisIndex, ::Anim::InterpolateIndex index, bool fades) const; - void drawAxis(Gen::AxisId axisIndex) const; - void drawTitle(Gen::AxisId axisIndex) const; - void drawDimensionLabels(Gen::AxisId axisIndex) const; + void drawAxis(Gen::AxisId axisIndex, + const Geom::AffineTransform &tr, + double w) const; + void drawTitle(Gen::AxisId axisIndex, + const Geom::AffineTransform &tr, + double w) const; + void drawDimensionLabels(Gen::AxisId axisIndex, + const Geom::AffineTransform &tr, + double w) const; void drawDimensionLabel(Gen::AxisId axisIndex, const Geom::Point &origo, const Interval &interval, + const Geom::AffineTransform &tr, double weight) const; }; diff --git a/src/chart/rendering/drawguides.cpp b/src/chart/rendering/drawguides.cpp index 6758b7651..1b0488118 100644 --- a/src/chart/rendering/drawguides.cpp +++ b/src/chart/rendering/drawguides.cpp @@ -13,7 +13,9 @@ namespace Vizzu::Draw { -void DrawGuides::draw(Gen::AxisId axisId) +void DrawGuides::draw(Gen::AxisId axisId, + const Geom::AffineTransform &tr, + double w) { const auto &guideStyle = parent.rootStyle.plot.getAxis(axisId).guides; @@ -27,8 +29,10 @@ void DrawGuides::draw(Gen::AxisId axisId) for (const auto &sep : parent.getSeparators(axisId)) drawGuide(axisId, sep.position, + tr, baseColor - * Math::FuzzyBool::And(sep.weight, + * Math::FuzzyBool::And(w, + sep.weight, parent.plot->guides.at(axisId).axisGuides)); parent.canvas.setLineWidth(0); @@ -37,17 +41,17 @@ void DrawGuides::draw(Gen::AxisId axisId) void DrawGuides::drawGuide(Gen::AxisId axisId, double val, + const Geom::AffineTransform &tr, const Gfx::Color &color) { - auto eventTarget = Events::Targets::axisGuide(axisId); - auto ident = Geom::Point::Ident(orientation(axisId)); auto normal = Geom::Point::Ident(!orientation(axisId)); auto relMax = ident * val; parent.canvas.setLineColor(color); - const Geom::Line line(relMax, relMax + normal); - if (parent.rootEvents.draw.plot.axis.guide->invoke( + auto line = tr(Geom::Line{relMax, relMax + normal}); + if (auto &&eventTarget = Events::Targets::axisGuide(axisId); + parent.rootEvents.draw.plot.axis.guide->invoke( Events::OnLineDrawEvent(*eventTarget, {line, true}))) { parent.painter.drawLine(line); parent.renderedChart.emplace(Line{line, true}, diff --git a/src/chart/rendering/drawguides.h b/src/chart/rendering/drawguides.h index 06ae8ad77..10568ca83 100644 --- a/src/chart/rendering/drawguides.h +++ b/src/chart/rendering/drawguides.h @@ -10,13 +10,16 @@ namespace Vizzu::Draw class DrawGuides { public: - void draw(Gen::AxisId axisId); + void draw(Gen::AxisId axisId, + const Geom::AffineTransform &tr, + double w); const DrawAxes &parent; private: void drawGuide(Gen::AxisId axisId, double val, + const Geom::AffineTransform &tr, const Gfx::Color &color); }; diff --git a/src/chart/rendering/drawinterlacing.cpp b/src/chart/rendering/drawinterlacing.cpp index 091f1a6e4..d7a2d2986 100644 --- a/src/chart/rendering/drawinterlacing.cpp +++ b/src/chart/rendering/drawinterlacing.cpp @@ -27,7 +27,9 @@ namespace Vizzu::Draw { -void DrawInterlacing::drawGeometries(Gen::AxisId axisIndex) const +void DrawInterlacing::drawGeometries(Gen::AxisId axisIndex, + const Geom::AffineTransform &tr, + double w) const { const auto &guides = parent.plot->guides.at(axisIndex); const auto &axisStyle = parent.rootStyle.plot.getAxis(axisIndex); @@ -54,14 +56,16 @@ void DrawInterlacing::drawGeometries(Gen::AxisId axisIndex) const const double &from, const double &to) { - return Geom::Rect{ - Geom::Point::Coord(orientation, clippedBottom, from), - {Geom::Size::Coord(orientation, + return Geom::Rect{tr(Geom::Point::Coord(orientation, + clippedBottom, + from)), + tr(Geom::Size::Coord(orientation, clippedSize, - to - from)}}; + to - from))}; }; - auto weight = Math::FuzzyBool::And(interval.weight, + auto weight = Math::FuzzyBool::And(w, + interval.weight, interval.isSecond, guides.interlacings); auto interlacingColor = *axisStyle.interlacing.color * weight; @@ -89,7 +93,9 @@ void DrawInterlacing::drawGeometries(Gen::AxisId axisIndex) const } } -void DrawInterlacing::drawTexts(Gen::AxisId axisIndex) const +void DrawInterlacing::drawTexts(Gen::AxisId axisIndex, + const Geom::AffineTransform &tr, + double w) const { const auto &axis = parent.getAxis(axisIndex).measure; auto orientation = !Gen::orientation(axisIndex); @@ -118,18 +124,22 @@ void DrawInterlacing::drawTexts(Gen::AxisId axisIndex) const drawDataLabel(axis.enabled, axisIndex, tickPos, + tr, *sep.label, axis.unit, - Math::FuzzyBool::And(sep.weight, + Math::FuzzyBool::And(w, + sep.weight, guides.labels)); if (needTick) drawSticks(tickLength, *axisStyle.ticks.color - * Math::FuzzyBool::And(sep.weight, + * Math::FuzzyBool::And(w, + sep.weight, guides.axisSticks), axisIndex, - tickPos); + tickPos, + tr); } } @@ -157,6 +167,7 @@ void DrawInterlacing::drawDataLabel( const ::Anim::Interpolated &axisEnabled, Gen::AxisId axisIndex, const Geom::Point &tickPos, + const Geom::AffineTransform &tr, double value, const ::Anim::String &unit, double alpha) const @@ -191,10 +202,10 @@ void DrawInterlacing::drawDataLabel( : labelStyle.side->factor( Styles::AxisLabel::Side::negative); - auto &&posDir = - parent.coordSys - .convertDirectionAt({refPos, refPos + normal}) - .extend(1 - 2 * under); + auto &&posDir = parent.coordSys + .convertDirectionAt(tr( + Geom::Line{refPos, refPos + normal})) + .extend(1 - 2 * under); auto &&wUnit = unit.get_or_first(index); auto str = Text::SmartString::fromPhysicalValue(value, @@ -218,7 +229,8 @@ void DrawInterlacing::drawDataLabel( void DrawInterlacing::drawSticks(double tickLength, const Gfx::Color &tickColor, Gen::AxisId axisIndex, - const Geom::Point &tickPos) const + const Geom::Point &tickPos, + const Geom::AffineTransform &tr) const { auto &canvas = parent.canvas; const auto &tickStyle = @@ -233,11 +245,11 @@ void DrawInterlacing::drawSticks(double tickLength, auto tickLine = tickStyle.position->combine( [tickLine = parent.coordSys - .convertDirectionAt({tickPos, + .convertDirectionAt(tr(Geom::Line{tickPos, tickPos + Geom::Point::Coord( !orientation(axisIndex), - -1.0)}) + -1.0)})) .segment(0, tickLength)](const auto &position) { switch (position) { diff --git a/src/chart/rendering/drawinterlacing.h b/src/chart/rendering/drawinterlacing.h index f27c10d73..17896ff58 100644 --- a/src/chart/rendering/drawinterlacing.h +++ b/src/chart/rendering/drawinterlacing.h @@ -10,8 +10,12 @@ namespace Vizzu::Draw class DrawInterlacing { public: - void drawGeometries(Gen::AxisId axisIndex) const; - void drawTexts(Gen::AxisId axisIndex) const; + void drawGeometries(Gen::AxisId axisIndex, + const Geom::AffineTransform &tr, + double w) const; + void drawTexts(Gen::AxisId axisIndex, + const Geom::AffineTransform &tr, + double w) const; const DrawAxes &parent; @@ -23,6 +27,7 @@ class DrawInterlacing void drawDataLabel(const ::Anim::Interpolated &enabled, Gen::AxisId axisIndex, const Geom::Point &tickPos, + const Geom::AffineTransform &tr, double value, const ::Anim::String &unit, double alpha) const; @@ -30,7 +35,8 @@ class DrawInterlacing void drawSticks(double tickLength, const Gfx::Color &tickColor, Gen::AxisId axisIndex, - const Geom::Point &tickPos) const; + const Geom::Point &tickPos, + const Geom::AffineTransform &tr) const; [[nodiscard]] std::map getInterlacingWeights( Gen::AxisId axisIndex) const; diff --git a/src/chart/rendering/drawplot.cpp b/src/chart/rendering/drawplot.cpp index 72ca72ae4..66d8b674a 100644 --- a/src/chart/rendering/drawplot.cpp +++ b/src/chart/rendering/drawplot.cpp @@ -27,7 +27,7 @@ void DrawPlot::draw(Gfx::ICanvas &canvas, drawPlotArea(canvas, painter, false); - auto axes = DrawAxes{{ctx()}, canvas, painter, {}, {}}.init(); + auto axes = DrawAxes{{ctx()}, canvas, painter, {}, {}, {}}.init(); axes.drawGeometries(); auto clip = rootStyle.plot.overflow == Styles::Overflow::hidden; From 3a3ba960442bd0c7252341b161a41dfd58245d7e Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Wed, 4 Dec 2024 16:30:49 +0100 Subject: [PATCH 03/11] Fix split chart when same dimension on main and sub axis --- CHANGELOG.md | 1 + src/chart/generator/plotbuilder.cpp | 16 ++++++---------- test/e2e/tests/fixes.json | 2 +- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef9cf4c68..e20f63564 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/src/chart/generator/plotbuilder.cpp b/src/chart/generator/plotbuilder.cpp index 6b6326b41..8b0ba95be 100644 --- a/src/chart/generator/plotbuilder.cpp +++ b/src/chart/generator/plotbuilder.cpp @@ -96,8 +96,8 @@ Buckets PlotBuilder::generateMarkers(std::size_t &mainBucketSize, if (plot->getOptions()->geometry == ShapeType::area) subIds.split_by(mainIds); - mainBucketSize = dataCube.combinedSizeOf(mainIds).first; - subBucketSize = dataCube.combinedSizeOf(subIds).first; + mainBucketSize = dataCube.combinedSizeOf(subIds).second; + subBucketSize = dataCube.combinedSizeOf(mainIds).second; plot->markers.reserve(dataCube.df->get_record_count()); } @@ -543,10 +543,9 @@ PlotBuilder::addSeparation(const Buckets &buckets, std::vector anyEnabled(otherBucketSize); for (auto &&bucket : buckets) - for (std::size_t i{}, prIx{}; auto &&[marker, idx] : bucket) { + for (auto &&[marker, idx] : bucket) { if (!marker.enabled) continue; - (i += idx.itemId - std::exchange(prIx, idx.itemId)) %= - ranges.size(); + auto i = idx.itemId; ranges[i].include(marker.getSizeBy(axisIndex).size()); anyEnabled[i] = true; } @@ -565,13 +564,10 @@ PlotBuilder::addSeparation(const Buckets &buckets, + (anyEnabled[i - 1] ? splitSpace : 0); for (auto &&bucket : buckets) - for (std::size_t i{}, prIx{}; auto &&[marker, idx] : bucket) { - (i += idx.itemId - std::exchange(prIx, idx.itemId)) %= - ranges.size(); + for (auto &&[marker, idx] : bucket) marker.setSizeBy(axisIndex, - Base::Align{align, ranges[i]}.getAligned( + Base::Align{align, ranges[idx.itemId]}.getAligned( marker.getSizeBy(axisIndex))); - } return {ranges, max}; } diff --git a/test/e2e/tests/fixes.json b/test/e2e/tests/fixes.json index 00c4b2230..94ca7b4f7 100644 --- a/test/e2e/tests/fixes.json +++ b/test/e2e/tests/fixes.json @@ -62,7 +62,7 @@ "refs": ["05540fd"] }, "42836788": { - "refs": ["17ef700"] + "refs": ["4d83fa2"] }, "47977099": { "refs": ["2845349"] From dfa75816983f0f8083f246a4f6cb719ab7fcbccb Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Fri, 6 Dec 2024 13:42:31 +0100 Subject: [PATCH 04/11] getMarkerBounds + merge fixes --- src/base/geom/rect.h | 13 +++++ src/chart/generator/axis.cpp | 22 ++++---- src/chart/generator/axis.h | 2 +- src/chart/generator/marker.cpp | 9 +-- src/chart/generator/plot.cpp | 16 ++++++ src/chart/generator/plot.h | 2 + src/chart/generator/plotbuilder.cpp | 86 +++++++++++++++-------------- src/chart/rendering/drawaxes.cpp | 8 +-- 8 files changed, 94 insertions(+), 64 deletions(-) diff --git a/src/base/geom/rect.h b/src/base/geom/rect.h index daf78f3a9..4e0091a70 100644 --- a/src/base/geom/rect.h +++ b/src/base/geom/rect.h @@ -56,6 +56,11 @@ struct Rect return {bottom(), top()}; } + [[nodiscard]] Math::Range<> oSize(Orientation o) + { + return isHorizontal(o) ? hSize() : vSize(); + } + [[nodiscard]] Math::Range<> x() const { return {pos.x, pos.x + size.x}; @@ -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()}; diff --git a/src/chart/generator/axis.cpp b/src/chart/generator/axis.cpp index 2d5310b11..f22d0c168 100644 --- a/src/chart/generator/axis.cpp +++ b/src/chart/generator/axis.cpp @@ -360,26 +360,26 @@ interpolate(const SplitAxis &op0, const SplitAxis &op1, double factor) { switch (type) { case Alg::union_call_t::only_left: { - auto from = lhs->second.range.getMin(); + auto from = lhs->second.range.min; res.parts[lhs->first] = { .weight = interpolate(lhs->second.weight, 0.0, factor), .range = interpolate(lhs->second.range, - Math::Range<>::Raw(from, from), + Math::Range<>{from, from}, factor)}; break; } case Alg::union_call_t::only_right: { - auto from = rhs->second.range.getMin(); + auto from = rhs->second.range.min; res.parts[rhs->first] = { .weight = interpolate(0.0, rhs->second.weight, factor), - .range = interpolate( - Math::Range<>::Raw(from, from), - rhs->second.range, - factor)}; + .range = + interpolate(Math::Range<>{from, from}, + rhs->second.range, + factor)}; break; } default: @@ -397,14 +397,14 @@ interpolate(const SplitAxis &op0, const SplitAxis &op1, double factor) res.parts[begin->first] = { .weight = interpolate(begin->second.weight, 1.0, factor), .range = interpolate(begin->second.range, - Math::Range<>::Raw(0, 1), + Math::Range<>{0, 1}, factor)}; while (++begin != op0.parts.end()) { res.parts[begin->first] = { .weight = interpolate(begin->second.weight, 0.0, factor), .range = interpolate(begin->second.range, - Math::Range<>::Raw(0, 1), + Math::Range<>{0, 1}, factor)}; } } @@ -412,14 +412,14 @@ interpolate(const SplitAxis &op0, const SplitAxis &op1, double factor) auto begin = op1.parts.begin(); res.parts[begin->first] = { .weight = interpolate(1.0, begin->second.weight, factor), - .range = interpolate(Math::Range<>::Raw(0, 1), + .range = interpolate(Math::Range<>{0, 1}, begin->second.range, factor)}; while (++begin != op1.parts.end()) { res.parts[begin->first] = { .weight = interpolate(0.0, begin->second.weight, factor), - .range = interpolate(Math::Range<>::Raw(0, 1), + .range = interpolate(Math::Range<>{0, 1}, begin->second.range, factor)}; } diff --git a/src/chart/generator/axis.h b/src/chart/generator/axis.h index bddd0b414..f99080533 100644 --- a/src/chart/generator/axis.h +++ b/src/chart/generator/axis.h @@ -201,7 +201,7 @@ struct SplitAxis : Axis struct Part { double weight{1.0}; - Math::Range<> range = Math::Range<>::Raw(0, 1); + Math::Range<> range{0, 1}; [[nodiscard]] bool operator==( const Part &other) const = default; diff --git a/src/chart/generator/marker.cpp b/src/chart/generator/marker.cpp index 90523d3b2..9a8fe6a83 100644 --- a/src/chart/generator/marker.cpp +++ b/src/chart/generator/marker.cpp @@ -226,17 +226,14 @@ void Marker::fromRectangle(const Geom::Rect &rect) Math::Range<> Marker::getSizeBy(AxisId axisId) const { - return isHorizontal(orientation(axisId)) ? toRectangle().hSize() - : toRectangle().vSize(); + return toRectangle().oSize(orientation(axisId)); + ; } void Marker::setSizeBy(AxisId axisId, const Math::Range<> range) { auto rect = toRectangle(); - if (isHorizontal(orientation(axisId))) - rect.setHSize(range); - else - rect.setVSize(range); + rect.setOSize(orientation(axisId), range); fromRectangle(rect); } diff --git a/src/chart/generator/plot.cpp b/src/chart/generator/plot.cpp index 98ac17d2c..4f3795dd2 100644 --- a/src/chart/generator/plot.cpp +++ b/src/chart/generator/plot.cpp @@ -68,6 +68,22 @@ bool Plot::isEmpty() const return options->getChannels().isEmpty(); } +Geom::Rect Plot::getMarkersBounds() const +{ + auto markerIt = markers.begin(); + while (markerIt != markers.end() + && !static_cast(markerIt->enabled)) + ++markerIt; + + if (markerIt == markers.end()) return {}; + + auto boundRect = markerIt->toRectangle().positive(); + while (++markerIt != markers.end()) + if (markerIt->enabled) + boundRect = boundRect.boundary(markerIt->toRectangle()); + return boundRect; +} + void Plot::prependMarkers(const Plot &plot) { auto it = markers.insert(markers.begin(), diff --git a/src/chart/generator/plot.h b/src/chart/generator/plot.h index dfed04e7d..a4557c562 100644 --- a/src/chart/generator/plot.h +++ b/src/chart/generator/plot.h @@ -84,6 +84,8 @@ class Plot void detachOptions(); [[nodiscard]] bool isEmpty() const; + [[nodiscard]] Geom::Rect getMarkersBounds() const; + static bool dimensionMatch(const Plot &a, const Plot &b); static bool hasMarkerChange(const Plot &source, const Plot &target); diff --git a/src/chart/generator/plotbuilder.cpp b/src/chart/generator/plotbuilder.cpp index 8b0ba95be..0dbc2de7a 100644 --- a/src/chart/generator/plotbuilder.cpp +++ b/src/chart/generator/plotbuilder.cpp @@ -101,11 +101,26 @@ Buckets PlotBuilder::generateMarkers(std::size_t &mainBucketSize, plot->markers.reserve(dataCube.df->get_record_count()); } - std::multimap map; - for (auto &&[ix, mid] : plot->getOptions()->markersInfo) - map.emplace(mid, ix); + struct CmpBySec + { + [[nodiscard]] bool operator()( + const std::pair &lhs, + const std::pair &rhs) const + { + return lhs.second < rhs.second; + } + }; + + auto &&set = + std::multiset>, + CmpBySec>{plot->getOptions()->markersInfo.begin(), + plot->getOptions()->markersInfo.end()}; - for (auto first = map.begin(); auto &&index : dataCube) + for (auto first = set.begin(); auto &&index : dataCube) for (auto &marker = plot->markers.emplace_back(*plot->getOptions(), dataCube, @@ -113,10 +128,12 @@ Buckets PlotBuilder::generateMarkers(std::size_t &mainBucketSize, mainIds, subIds, index, - map.contains(index.marker_id)); - first != map.end() && first->first == marker.idx; + first != set.end() + && first->get().second == index.marker_id); + first != set.end() + && first->get().second == index.marker_id; ++first) - plot->markersInfo.insert({first->second, + plot->markersInfo.insert({first->get().first, Plot::MarkerInfo{Plot::MarkerInfoContent{marker}}}); if (!std::ranges::is_sorted(plot->markers, {}, &Marker::idx)) @@ -308,59 +325,44 @@ void PlotBuilder::calcAxises(const Data::DataTable &dataTable, const std::size_t &mainBucketSize, const std::size_t &subBucketSize) { + auto mainAxis = plot->getOptions()->mainAxisType(); auto &&[subRanges, subMax] = addSeparation(buckets, plot->getOptions()->subAxisType(), mainBucketSize); auto &&[mainRanges, mainMax] = addSeparation(buckets.sort(&Marker::mainId), - plot->getOptions()->mainAxisType(), + mainAxis, subBucketSize); const auto &xrange = plot->getOptions()->getHorizontalAxis().range; const auto &yrange = plot->getOptions()->getVerticalAxis().range; - auto markerIt = plot->markers.begin(); - while (markerIt != plot->markers.end() - && !static_cast(markerIt->enabled)) - ++markerIt; - - if (markerIt == plot->markers.end()) { - stats.setIfRange(AxisId::x, xrange.getRange({0.0, 0.0})); - stats.setIfRange(AxisId::y, xrange.getRange({0.0, 0.0})); - } - else { - auto boundRect = markerIt->toRectangle().positive(); - - while (++markerIt != plot->markers.end()) { - if (!markerIt->enabled) continue; - boundRect = boundRect.boundary(markerIt->toRectangle()); - } - - plot->getOptions()->setAutoRange( - !std::signbit(boundRect.hSize().min), - !std::signbit(boundRect.vSize().min)); + auto boundRect = plot->getMarkersBounds(); - boundRect.setHSize(xrange.getRange(boundRect.hSize())); - boundRect.setVSize(yrange.getRange(boundRect.vSize())); + plot->getOptions()->setAutoRange( + !std::signbit(boundRect.hSize().min), + !std::signbit(boundRect.vSize().min)); - for (auto &marker : plot->markers) { - if (!boundRect.positive().intersects( - marker.toRectangle().positive())) - marker.enabled = false; + boundRect.setHSize(xrange.getRange(boundRect.hSize())); + boundRect.setVSize(yrange.getRange(boundRect.vSize())); - auto rect = marker.toRectangle(); - auto newRect = boundRect.normalize(rect); - marker.fromRectangle(newRect); - } + for (auto &marker : plot->markers) { + if (!boundRect.positive().intersects( + marker.toRectangle().positive())) + marker.enabled = false; - stats.setIfRange(AxisId::x, - {boundRect.left(), boundRect.right()}); - stats.setIfRange(AxisId::y, - {boundRect.bottom(), boundRect.top()}); + auto rect = marker.toRectangle(); + auto newRect = boundRect.normalize(rect); + marker.fromRectangle(newRect); } + stats.setIfRange(AxisId::x, + {boundRect.left(), boundRect.right()}); + stats.setIfRange(AxisId::y, + {boundRect.bottom(), boundRect.top()}); + for (const AxisId &ch : {AxisId::x, AxisId::y}) calcAxis(dataTable, ch); } diff --git a/src/chart/rendering/drawaxes.cpp b/src/chart/rendering/drawaxes.cpp index c138cb8f0..f1e16b55f 100644 --- a/src/chart/rendering/drawaxes.cpp +++ b/src/chart/rendering/drawaxes.cpp @@ -48,10 +48,10 @@ void DrawAxes::drawGeometries() const Geom::AffineTransform tr{xSplit.range.size(), 0.0, - xSplit.range.getMin(), + xSplit.range.min, 0.0, ySplit.range.size(), - ySplit.range.getMin()}; + ySplit.range.min}; DrawInterlacing{*this}.drawGeometries(Gen::AxisId::y, tr, @@ -79,10 +79,10 @@ void DrawAxes::drawLabels() const Geom::AffineTransform tr{xSplit.range.size(), 0.0, - xSplit.range.getMin(), + xSplit.range.min, 0.0, ySplit.range.size(), - ySplit.range.getMin()}; + ySplit.range.min}; DrawInterlacing{*this}.drawTexts(Gen::AxisId::y, tr, From 95073d61fda21d4b97da689d7bb3417417d2fbbb Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Fri, 6 Dec 2024 13:50:34 +0100 Subject: [PATCH 05/11] move negative split to upper --- src/chart/generator/plotbuilder.cpp | 10 +++++++--- test/e2e/tests/tickets.json | 2 +- test/e2e/tests/tickets/146.mjs | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/chart/generator/plotbuilder.cpp b/src/chart/generator/plotbuilder.cpp index 0dbc2de7a..c6274ceeb 100644 --- a/src/chart/generator/plotbuilder.cpp +++ b/src/chart/generator/plotbuilder.cpp @@ -561,9 +561,13 @@ PlotBuilder::addSeparation(const Buckets &buckets, max.size(), plot->getStyle().calculatedSize()); - for (auto i = 1U; i < ranges.size(); ++i) - ranges[i] = ranges[i] + ranges[i - 1].max - + (anyEnabled[i - 1] ? splitSpace : 0); + auto onMax = ranges[0].max; + ranges[0] = ranges[0] - ranges[0].min; + for (auto i = 1U; i < ranges.size(); ++i) { + onMax += anyEnabled[i - 1] ? splitSpace : 0; + ranges[i] = ranges[i] + onMax - ranges[i].min * 2; + onMax += ranges[i].size(); + } for (auto &&bucket : buckets) for (auto &&[marker, idx] : bucket) diff --git a/test/e2e/tests/tickets.json b/test/e2e/tests/tickets.json index 58c9db713..773a70a11 100644 --- a/test/e2e/tests/tickets.json +++ b/test/e2e/tests/tickets.json @@ -11,7 +11,7 @@ "refs": ["1928a0a"] }, "146": { - "refs": ["e656fba"] + "refs": ["777e2e4"] }, "255": { "refs": ["29b4a25"] diff --git a/test/e2e/tests/tickets/146.mjs b/test/e2e/tests/tickets/146.mjs index 1b88cf892..2b9682c37 100644 --- a/test/e2e/tests/tickets/146.mjs +++ b/test/e2e/tests/tickets/146.mjs @@ -15,7 +15,7 @@ const testSteps = [ { name: 'Val', type: 'measure', - values: [3, -5, 4, -4, 6, 5, -5, 7, 6] + values: [3, -5, 4, -4, 6, 5, -3, 7, 6] } ] } From 145694b9242355d2c7b3441365856fd920437b1d Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Fri, 6 Dec 2024 16:20:54 +0100 Subject: [PATCH 06/11] separate marker setSizeBy --- src/base/geom/rect.cpp | 18 ------------------ src/base/geom/rect.h | 3 +-- src/base/math/range.h | 14 ++++++++++++++ src/chart/generator/marker.cpp | 13 +++---------- src/chart/generator/marker.h | 1 - src/chart/generator/plotbuilder.cpp | 24 ++++++++++++------------ test/e2e/test_cases/test_cases.json | 10 +++++----- test/e2e/tests/fixes.json | 2 +- 8 files changed, 36 insertions(+), 49 deletions(-) diff --git a/src/base/geom/rect.cpp b/src/base/geom/rect.cpp index 18ad2fecc..ae94afe1d 100644 --- a/src/base/geom/rect.cpp +++ b/src/base/geom/rect.cpp @@ -91,24 +91,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) diff --git a/src/base/geom/rect.h b/src/base/geom/rect.h index 4e0091a70..343fa5a90 100644 --- a/src/base/geom/rect.h +++ b/src/base/geom/rect.h @@ -56,7 +56,7 @@ struct Rect return {bottom(), top()}; } - [[nodiscard]] Math::Range<> oSize(Orientation o) + [[nodiscard]] Math::Range<> oSize(Orientation o) const { return isHorizontal(o) ? hSize() : vSize(); } @@ -151,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 diff --git a/src/base/math/range.h b/src/base/math/range.h index 0353e69d3..7a62ac15d 100644 --- a/src/base/math/range.h +++ b/src/base/math/range.h @@ -122,6 +122,20 @@ template struct Range [[nodiscard]] T size() const { return max - min; } + [[nodiscard]] bool intersects(const Range<> &range) const + { + using Floating::is_zero; + auto first = std::strong_order(max, range.min); + auto second = std::strong_order(range.max, min); + + auto isOutside = + is_lt(first) || is_lt(second) + || ((is_eq(first) || is_eq(second)) && !is_zero(size()) + && !is_zero(range.size())); + + return !isOutside; + } + T min{std::numeric_limits::max()}; T max{std::numeric_limits::lowest()}; }; diff --git a/src/chart/generator/marker.cpp b/src/chart/generator/marker.cpp index 9a8fe6a83..90ebcc953 100644 --- a/src/chart/generator/marker.cpp +++ b/src/chart/generator/marker.cpp @@ -218,23 +218,16 @@ Geom::Rect Marker::toRectangle() const return {position - size, {size}}; } -void Marker::fromRectangle(const Geom::Rect &rect) -{ - position = rect.pos + rect.size; - size = rect.size; -} - Math::Range<> Marker::getSizeBy(AxisId axisId) const { return toRectangle().oSize(orientation(axisId)); - ; } void Marker::setSizeBy(AxisId axisId, const Math::Range<> range) { - auto rect = toRectangle(); - rect.setOSize(orientation(axisId), range); - fromRectangle(rect); + auto o = orientation(axisId); + position.getCoord(o) = range.max; + size.getCoord(o) = range.size(); } bool Marker::Label::operator==(const Label &other) const diff --git a/src/chart/generator/marker.h b/src/chart/generator/marker.h index 664c9b31d..45b336348 100644 --- a/src/chart/generator/marker.h +++ b/src/chart/generator/marker.h @@ -83,7 +83,6 @@ class Marker bool polarConnection); [[nodiscard]] Geom::Rect toRectangle() const; - void fromRectangle(const Geom::Rect &rect); [[nodiscard]] Math::Range<> getSizeBy(AxisId axisId) const; void setSizeBy(AxisId axisId, Math::Range<> range); diff --git a/src/chart/generator/plotbuilder.cpp b/src/chart/generator/plotbuilder.cpp index c6274ceeb..642d8aa4a 100644 --- a/src/chart/generator/plotbuilder.cpp +++ b/src/chart/generator/plotbuilder.cpp @@ -348,20 +348,20 @@ void PlotBuilder::calcAxises(const Data::DataTable &dataTable, boundRect.setHSize(xrange.getRange(boundRect.hSize())); boundRect.setVSize(yrange.getRange(boundRect.vSize())); - for (auto &marker : plot->markers) { - if (!boundRect.positive().intersects( - marker.toRectangle().positive())) - marker.enabled = false; + for (auto &&[axis, ranges] : + {std::pair{mainAxis, &mainRanges}, {!mainAxis, &subRanges}}) { + auto o = orientation(axis); + for (auto &marker : plot->markers) { + if (!boundRect.positive().oSize(o).intersects( + marker.toRectangle().positive().oSize(o))) + marker.enabled = false; - auto rect = marker.toRectangle(); - auto newRect = boundRect.normalize(rect); - marker.fromRectangle(newRect); - } + marker.setSizeBy(axis, + boundRect.normalize(marker.toRectangle()).oSize(o)); + } - stats.setIfRange(AxisId::x, - {boundRect.left(), boundRect.right()}); - stats.setIfRange(AxisId::y, - {boundRect.bottom(), boundRect.top()}); + stats.setIfRange(axis, boundRect.oSize(o)); + } for (const AxisId &ch : {AxisId::x, AxisId::y}) calcAxis(dataTable, ch); diff --git a/test/e2e/test_cases/test_cases.json b/test/e2e/test_cases/test_cases.json index 431a79d05..93073661b 100644 --- a/test/e2e/test_cases/test_cases.json +++ b/test/e2e/test_cases/test_cases.json @@ -38,13 +38,13 @@ "refs": ["e187f48"] }, "basic_animations/labels/marker/area_2dis_3con": { - "refs": ["cd8bd6d"] + "refs": ["3711b35"] }, "basic_animations/labels/marker/circle_negative_2dis_3con": { "refs": ["6df75bb"] }, "basic_animations/labels/marker/line_2dis_3con": { - "refs": ["097888e"] + "refs": ["9427821"] }, "basic_animations/labels/marker/padding_test_rectangle_negative_2dis_3con": { "refs": ["005e51c"] @@ -53,7 +53,7 @@ "refs": ["da55724"] }, "basic_animations/labels/rectangle_labels_rotated_charts": { - "refs": ["10761b9"] + "refs": ["ab6aa1d"] }, "basic_animations/legend_transitions/color_2discrete_anim": { "refs": ["fed1ebe"] @@ -1367,7 +1367,7 @@ "refs": ["0093ba5"] }, "ww_animTiming/without-polar/02_w-p_c-r-c": { - "refs": ["0b783fc"] + "refs": ["611dc70"] }, "ww_animTiming/without-polar/05_w-p_r-c-r": { "refs": ["b02225c"] @@ -3092,7 +3092,7 @@ "refs": ["b91f870"] }, "ww_samples_for_presets/polar_coo_sys/41_P_R_multi-level_pie_chart": { - "refs": ["cb896ab"] + "refs": ["0471993"] }, "web_content/presets_config/chart/column_polar_stacked": { "refs": ["369b38b"] diff --git a/test/e2e/tests/fixes.json b/test/e2e/tests/fixes.json index 94ca7b4f7..418a2b9cf 100644 --- a/test/e2e/tests/fixes.json +++ b/test/e2e/tests/fixes.json @@ -35,7 +35,7 @@ "refs": ["e4b8a2f"] }, "327": { - "refs": ["8eb7c5d"] + "refs": ["6c0099b"] }, "333": { "refs": ["8135d80"] From cc3bd4ea81106432641ff3776076d5cc44056c42 Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Fri, 6 Dec 2024 17:30:55 +0100 Subject: [PATCH 07/11] Remove marker toRectangle --- src/base/math/range.h | 20 +++---- src/chart/generator/marker.cpp | 9 +-- src/chart/generator/marker.h | 2 - src/chart/generator/plot.cpp | 8 +-- src/chart/generator/plot.h | 2 +- src/chart/generator/plotbuilder.cpp | 56 ++++++++++--------- src/chart/generator/plotbuilder.h | 4 +- src/chart/main/stylesheet.cpp | 2 +- .../rectangle_labels_rotated_charts.mjs | 4 +- test/e2e/test_cases/test_cases.json | 14 ++--- .../rotated_bar_to_donut.mjs | 3 +- 11 files changed, 59 insertions(+), 65 deletions(-) diff --git a/src/base/math/range.h b/src/base/math/range.h index 7a62ac15d..d864b5a85 100644 --- a/src/base/math/range.h +++ b/src/base/math/range.h @@ -45,15 +45,10 @@ template struct Range return !less(value, min) && !less(max, value); } - [[nodiscard]] bool includes(const Range &range) const - { - return !less(range.max, min) && !less(max, range.min); - } - - [[nodiscard]] T rescale(const T &value) const + [[nodiscard]] T rescale(const T &value, T def = 0.5) const { auto s = size(); - return is_zero(s) ? 0.5 : (value - min) / s; + return is_zero(s) ? def : (value - min) / s; } [[nodiscard]] T scale(const T &value) const @@ -71,11 +66,6 @@ template struct Range return is_zero(max) ? 0 : value / max; } - [[nodiscard]] Range normalize(const Range &range) const - { - return {normalize(range.min), normalize(range.max)}; - } - bool operator==(const Range &other) const { return min == other.min && max == other.max; @@ -136,6 +126,12 @@ template struct Range return !isOutside; } + [[nodiscard]] Range positive() const + { + auto &&[min, max] = std::minmax(this->min, this->max, less); + return {min, max}; + } + T min{std::numeric_limits::max()}; T max{std::numeric_limits::lowest()}; }; diff --git a/src/chart/generator/marker.cpp b/src/chart/generator/marker.cpp index 90ebcc953..f3050df38 100644 --- a/src/chart/generator/marker.cpp +++ b/src/chart/generator/marker.cpp @@ -213,14 +213,11 @@ double Marker::getValueForChannel(const Channels &channels, return value; } -Geom::Rect Marker::toRectangle() const -{ - return {position - size, {size}}; -} - Math::Range<> Marker::getSizeBy(AxisId axisId) const { - return toRectangle().oSize(orientation(axisId)); + auto o = orientation(axisId); + return {position.getCoord(o) - size.getCoord(o), + position.getCoord(o)}; } void Marker::setSizeBy(AxisId axisId, const Math::Range<> range) diff --git a/src/chart/generator/marker.h b/src/chart/generator/marker.h index 45b336348..9cb1ce589 100644 --- a/src/chart/generator/marker.h +++ b/src/chart/generator/marker.h @@ -82,8 +82,6 @@ class Marker bool main, bool polarConnection); - [[nodiscard]] Geom::Rect toRectangle() const; - [[nodiscard]] Math::Range<> getSizeBy(AxisId axisId) const; void setSizeBy(AxisId axisId, Math::Range<> range); diff --git a/src/chart/generator/plot.cpp b/src/chart/generator/plot.cpp index 4f3795dd2..619e700b1 100644 --- a/src/chart/generator/plot.cpp +++ b/src/chart/generator/plot.cpp @@ -68,19 +68,19 @@ bool Plot::isEmpty() const return options->getChannels().isEmpty(); } -Geom::Rect Plot::getMarkersBounds() const +Math::Range<> Plot::getMarkersBounds(AxisId axisId) const { auto markerIt = markers.begin(); while (markerIt != markers.end() && !static_cast(markerIt->enabled)) ++markerIt; - if (markerIt == markers.end()) return {}; + if (markerIt == markers.end()) return {{}, {}}; - auto boundRect = markerIt->toRectangle().positive(); + auto boundRect = markerIt->getSizeBy(axisId).positive(); while (++markerIt != markers.end()) if (markerIt->enabled) - boundRect = boundRect.boundary(markerIt->toRectangle()); + boundRect.include(markerIt->getSizeBy(axisId)); return boundRect; } diff --git a/src/chart/generator/plot.h b/src/chart/generator/plot.h index a4557c562..61f7bf34a 100644 --- a/src/chart/generator/plot.h +++ b/src/chart/generator/plot.h @@ -84,7 +84,7 @@ class Plot void detachOptions(); [[nodiscard]] bool isEmpty() const; - [[nodiscard]] Geom::Rect getMarkersBounds() const; + [[nodiscard]] Math::Range<> getMarkersBounds(AxisId axisId) const; static bool dimensionMatch(const Plot &a, const Plot &b); static bool hasMarkerChange(const Plot &source, diff --git a/src/chart/generator/plotbuilder.cpp b/src/chart/generator/plotbuilder.cpp index 642d8aa4a..18e3dba2d 100644 --- a/src/chart/generator/plotbuilder.cpp +++ b/src/chart/generator/plotbuilder.cpp @@ -326,41 +326,45 @@ void PlotBuilder::calcAxises(const Data::DataTable &dataTable, const std::size_t &subBucketSize) { auto mainAxis = plot->getOptions()->mainAxisType(); - auto &&[subRanges, subMax] = addSeparation(buckets, - plot->getOptions()->subAxisType(), - mainBucketSize); + auto &&subRanges = + addSeparation(buckets, !mainAxis, mainBucketSize); - auto &&[mainRanges, mainMax] = - addSeparation(buckets.sort(&Marker::mainId), - mainAxis, - subBucketSize); + auto &&mainRanges = addSeparation(buckets.sort(&Marker::mainId), + mainAxis, + subBucketSize); - const auto &xrange = - plot->getOptions()->getHorizontalAxis().range; - const auto &yrange = plot->getOptions()->getVerticalAxis().range; + auto mainBoundRect = plot->getMarkersBounds(mainAxis); + auto subBoundRect = plot->getMarkersBounds(!mainAxis); + if (mainAxis != AxisId::x) std::swap(mainBoundRect, subBoundRect); - auto boundRect = plot->getMarkersBounds(); + plot->getOptions()->setAutoRange(!std::signbit(mainBoundRect.min), + !std::signbit(subBoundRect.min)); - plot->getOptions()->setAutoRange( - !std::signbit(boundRect.hSize().min), - !std::signbit(boundRect.vSize().min)); + if (mainAxis != AxisId::x) std::swap(mainBoundRect, subBoundRect); - boundRect.setHSize(xrange.getRange(boundRect.hSize())); - boundRect.setVSize(yrange.getRange(boundRect.vSize())); + mainBoundRect = + plot->getOptions()->getChannels().at(mainAxis).range.getRange( + mainBoundRect); + subBoundRect = plot->getOptions() + ->getChannels() + .at(!mainAxis) + .range.getRange(subBoundRect); - for (auto &&[axis, ranges] : - {std::pair{mainAxis, &mainRanges}, {!mainAxis, &subRanges}}) { - auto o = orientation(axis); + for (auto &&[axis, ranges, boundSize] : + {std::tuple{mainAxis, &mainRanges, std::move(mainBoundRect)}, + {!mainAxis, &subRanges, std::move(subBoundRect)}}) { for (auto &marker : plot->markers) { - if (!boundRect.positive().oSize(o).intersects( - marker.toRectangle().positive().oSize(o))) + auto &&markerSize = marker.getSizeBy(axis); + if (!boundSize.positive().intersects( + markerSize.positive())) marker.enabled = false; marker.setSizeBy(axis, - boundRect.normalize(marker.toRectangle()).oSize(o)); + {boundSize.rescale(markerSize.min, 0.0), + boundSize.rescale(markerSize.max, 0.0)}); } - stats.setIfRange(axis, boundRect.oSize(o)); + stats.setIfRange(axis, boundSize); } for (const AxisId &ch : {AxisId::x, AxisId::y}) @@ -530,8 +534,8 @@ void PlotBuilder::addAlignment(const Buckets &buckets, } } -std::pair>, Math::Range<>> -PlotBuilder::addSeparation(const Buckets &buckets, +std::vector> PlotBuilder::addSeparation( + const Buckets &buckets, AxisId axisIndex, const std::size_t &otherBucketSize) const { @@ -575,7 +579,7 @@ PlotBuilder::addSeparation(const Buckets &buckets, Base::Align{align, ranges[idx.itemId]}.getAligned( marker.getSizeBy(axisIndex))); - return {ranges, max}; + return ranges; } void PlotBuilder::normalizeSizes() diff --git a/src/chart/generator/plotbuilder.h b/src/chart/generator/plotbuilder.h index 34cc490a6..b7ab2b25a 100644 --- a/src/chart/generator/plotbuilder.h +++ b/src/chart/generator/plotbuilder.h @@ -46,8 +46,8 @@ class PlotBuilder void calcLegendAndLabel(const Data::DataTable &dataTable); void calcAxis(const Data::DataTable &dataTable, AxisId type); void addAlignment(const Buckets &buckets, AxisId axisIndex) const; - [[nodiscard]] std::pair>, Math::Range<>> - addSeparation(const Buckets &buckets, + [[nodiscard]] std::vector> addSeparation( + const Buckets &buckets, AxisId axisIndex, const std::size_t &otherBucketSize) const; void normalizeSizes(); diff --git a/src/chart/main/stylesheet.cpp b/src/chart/main/stylesheet.cpp index 8faa306e1..6d5481f04 100644 --- a/src/chart/main/stylesheet.cpp +++ b/src/chart/main/stylesheet.cpp @@ -258,7 +258,7 @@ void Sheet::setAfterStyles(Gen::Plot &plot, const Geom::Size &size) ranges.end(), [&next_range](const Math::Range<> &other) { - return other.includes(next_range); + return other.intersects(next_range); })) { has_collision = true; break; diff --git a/test/e2e/test_cases/basic_animations/labels/rectangle_labels_rotated_charts.mjs b/test/e2e/test_cases/basic_animations/labels/rectangle_labels_rotated_charts.mjs index 9ddfe5822..f19744a49 100755 --- a/test/e2e/test_cases/basic_animations/labels/rectangle_labels_rotated_charts.mjs +++ b/test/e2e/test_cases/basic_animations/labels/rectangle_labels_rotated_charts.mjs @@ -189,7 +189,7 @@ const testSteps = [ config: { channels: { y: { detach: ['Value 2 (+)'], range: { min: 'auto', max: 'auto' } }, - x: { attach: ['Value 2 (+)'] }, + x: { attach: ['Value 2 (+)'], range: { max: '100%' } }, label: { attach: ['Country'] } }, title: 'Polar Coordinate', @@ -340,7 +340,7 @@ const testSteps = [ chart.animate({ config: { channels: { - x: { detach: ['Value 2 (+)', 'Country'] }, + x: { detach: ['Value 2 (+)', 'Country'], range: { max: 'auto' } }, size: { attach: ['Value 2 (+)'] } }, title: 'Without Coordinate', diff --git a/test/e2e/test_cases/test_cases.json b/test/e2e/test_cases/test_cases.json index 93073661b..94b6199dd 100644 --- a/test/e2e/test_cases/test_cases.json +++ b/test/e2e/test_cases/test_cases.json @@ -38,22 +38,22 @@ "refs": ["e187f48"] }, "basic_animations/labels/marker/area_2dis_3con": { - "refs": ["3711b35"] + "refs": ["b543e3d"] }, "basic_animations/labels/marker/circle_negative_2dis_3con": { - "refs": ["6df75bb"] + "refs": ["7c4e5cc"] }, "basic_animations/labels/marker/line_2dis_3con": { - "refs": ["9427821"] + "refs": ["fc92094"] }, "basic_animations/labels/marker/padding_test_rectangle_negative_2dis_3con": { - "refs": ["005e51c"] + "refs": ["68d1990"] }, "basic_animations/labels/marker/rectangle_negative_2dis_3con": { - "refs": ["da55724"] + "refs": ["cf62886"] }, "basic_animations/labels/rectangle_labels_rotated_charts": { - "refs": ["ab6aa1d"] + "refs": ["3e62c90"] }, "basic_animations/legend_transitions/color_2discrete_anim": { "refs": ["fed1ebe"] @@ -395,7 +395,7 @@ "refs": ["d8ffda3"] }, "static_chart_types/polar_coo_sys/coxcomb_stacked_rectangle_2dis_2con": { - "refs": ["80fac6a"] + "refs": ["19ccbba"] }, "static_chart_types/polar_coo_sys/radial_rectangle_1dis_1con": { "refs": ["ffbd29a"] diff --git a/test/e2e/test_cases/ww_noFade/wNoFade_Tests/Marker_label_problem/rotated_bar_to_donut.mjs b/test/e2e/test_cases/ww_noFade/wNoFade_Tests/Marker_label_problem/rotated_bar_to_donut.mjs index 06969ac3a..2e5b9cbc9 100644 --- a/test/e2e/test_cases/ww_noFade/wNoFade_Tests/Marker_label_problem/rotated_bar_to_donut.mjs +++ b/test/e2e/test_cases/ww_noFade/wNoFade_Tests/Marker_label_problem/rotated_bar_to_donut.mjs @@ -26,7 +26,7 @@ const testSteps = [ data, config: { channels: { - y: { set: ['Value 5 (+/-)'], range: { min: '0%', max: '110%' } }, + y: { set: ['Value 5 (+/-)'] }, x: 'Country', color: 'Country', label: 'Value 5 (+/-)' @@ -40,7 +40,6 @@ const testSteps = [ chart.animate({ config: { channels: { - y: { range: { min: 'auto', max: 'auto' } }, x: ['Country', 'Value 2 (+)'] }, title: 'Polar Coordinate', From 50b3b1ac994c141dfa972120e90ec5e79ecafc52 Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Fri, 6 Dec 2024 17:38:27 +0100 Subject: [PATCH 08/11] clang-tidy --- src/base/alg/union_foreach.h | 1 + src/chart/generator/plotbuilder.cpp | 25 ++++++++++++------------- src/chart/options/options.cpp | 3 +-- src/chart/rendering/drawaxes.cpp | 16 ++++++++-------- 4 files changed, 22 insertions(+), 23 deletions(-) diff --git a/src/base/alg/union_foreach.h b/src/base/alg/union_foreach.h index 375b9e036..dbb444e5c 100644 --- a/src/base/alg/union_foreach.h +++ b/src/base/alg/union_foreach.h @@ -1,6 +1,7 @@ #ifndef ALG_UNION_FOREACH_H #define ALG_UNION_FOREACH_H +#include #include #include "union_foreach.h" diff --git a/src/chart/generator/plotbuilder.cpp b/src/chart/generator/plotbuilder.cpp index 18e3dba2d..dec00919b 100644 --- a/src/chart/generator/plotbuilder.cpp +++ b/src/chart/generator/plotbuilder.cpp @@ -335,24 +335,23 @@ void PlotBuilder::calcAxises(const Data::DataTable &dataTable, auto mainBoundRect = plot->getMarkersBounds(mainAxis); auto subBoundRect = plot->getMarkersBounds(!mainAxis); - if (mainAxis != AxisId::x) std::swap(mainBoundRect, subBoundRect); - plot->getOptions()->setAutoRange(!std::signbit(mainBoundRect.min), - !std::signbit(subBoundRect.min)); - - if (mainAxis != AxisId::x) std::swap(mainBoundRect, subBoundRect); + plot->getOptions()->setAutoRange( + !std::signbit( + (mainAxis == AxisId::x ? mainBoundRect : subBoundRect) + .min), + !std::signbit( + (mainAxis == AxisId::x ? subBoundRect : mainBoundRect) + .min)); mainBoundRect = - plot->getOptions()->getChannels().at(mainAxis).range.getRange( - mainBoundRect); - subBoundRect = plot->getOptions() - ->getChannels() - .at(!mainAxis) - .range.getRange(subBoundRect); + plot->getOptions()->mainAxis().range.getRange(mainBoundRect); + subBoundRect = + plot->getOptions()->subAxis().range.getRange(subBoundRect); for (auto &&[axis, ranges, boundSize] : - {std::tuple{mainAxis, &mainRanges, std::move(mainBoundRect)}, - {!mainAxis, &subRanges, std::move(subBoundRect)}}) { + {std::tuple{mainAxis, &mainRanges, mainBoundRect}, + {!mainAxis, &subRanges, subBoundRect}}) { for (auto &marker : plot->markers) { auto &&markerSize = marker.getSizeBy(axis); if (!boundSize.positive().intersects( diff --git a/src/chart/options/options.cpp b/src/chart/options/options.cpp index ba5c5cdd1..e1954ca8f 100644 --- a/src/chart/options/options.cpp +++ b/src/chart/options/options.cpp @@ -220,8 +220,7 @@ AxisId Options::getHorizontalChannel() const AxisId Options::getVerticalChannel() const { - return getHorizontalChannel() == AxisId::x ? AxisId::y - : AxisId::x; + return !getHorizontalChannel(); } bool Options::isShapeValid(const ShapeType &shapeType) const diff --git a/src/chart/rendering/drawaxes.cpp b/src/chart/rendering/drawaxes.cpp index f1e16b55f..0ea10c919 100644 --- a/src/chart/rendering/drawaxes.cpp +++ b/src/chart/rendering/drawaxes.cpp @@ -39,14 +39,14 @@ namespace Vizzu::Draw void DrawAxes::drawGeometries() const { - for (auto &xSplit : std::views::values(splits[Gen::AxisId::x])) - for (auto &ySplit : + for (auto &&xSplit : std::views::values(splits[Gen::AxisId::x])) + for (auto &&ySplit : std::views::values(splits[Gen::AxisId::y])) { - double weight = + auto weight = Math::FuzzyBool::And(xSplit.weight, ySplit.weight); if (Math::Floating::is_zero(weight)) continue; - Geom::AffineTransform tr{xSplit.range.size(), + const Geom::AffineTransform tr{xSplit.range.size(), 0.0, xSplit.range.min, 0.0, @@ -70,14 +70,14 @@ void DrawAxes::drawGeometries() const void DrawAxes::drawLabels() const { - for (auto &xSplit : std::views::values(splits[Gen::AxisId::x])) - for (auto &ySplit : + for (auto &&xSplit : std::views::values(splits[Gen::AxisId::x])) + for (auto &&ySplit : std::views::values(splits[Gen::AxisId::y])) { - double weight = + auto weight = Math::FuzzyBool::And(xSplit.weight, ySplit.weight); if (Math::Floating::is_zero(weight)) continue; - Geom::AffineTransform tr{xSplit.range.size(), + const Geom::AffineTransform tr{xSplit.range.size(), 0.0, xSplit.range.min, 0.0, From fbf039aab2e128050d5ec1e0a1e7ad7134ddce04 Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Fri, 6 Dec 2024 18:14:33 +0100 Subject: [PATCH 09/11] fix tests --- test/e2e/test_cases/test_cases.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/e2e/test_cases/test_cases.json b/test/e2e/test_cases/test_cases.json index 94b6199dd..9885d08a2 100644 --- a/test/e2e/test_cases/test_cases.json +++ b/test/e2e/test_cases/test_cases.json @@ -53,7 +53,7 @@ "refs": ["cf62886"] }, "basic_animations/labels/rectangle_labels_rotated_charts": { - "refs": ["3e62c90"] + "refs": ["b79fb18"] }, "basic_animations/legend_transitions/color_2discrete_anim": { "refs": ["fed1ebe"] @@ -2441,7 +2441,7 @@ "refs": ["ecb5f2b"] }, "ww_noFade/wNoFade_Tests/Marker_label_problem/rotated_bar_to_donut": { - "refs": ["6000b24"] + "refs": ["9196915"] }, "ww_noFade/wNoFade_Tests/Marker_transition_problem/Bubble_Stacked_Bubble_to_Area": { "refs": ["295cd11"] From b52a9c377e0f30f6068ed29393420487caa816b7 Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Fri, 6 Dec 2024 18:51:38 +0100 Subject: [PATCH 10/11] some clang-tidy + first try to purge horizontalAxis/verticalAxis --- src/base/alg/union_foreach.h | 7 +++---- src/base/geom/point.h | 5 +++-- src/base/geom/rect.cpp | 1 - src/chart/generator/axis.cpp | 1 - src/chart/generator/marker.cpp | 2 -- src/chart/generator/plot.cpp | 2 ++ src/chart/generator/plotbuilder.cpp | 3 ++- src/chart/main/style.h | 5 +++++ src/chart/main/stylesheet.cpp | 26 ++++++++++++++----------- src/chart/options/options.cpp | 14 +++++-------- src/chart/options/options.h | 21 -------------------- src/chart/rendering/drawaxes.cpp | 1 + src/chart/rendering/drawguides.cpp | 1 + src/chart/rendering/drawinterlacing.cpp | 3 ++- test/unit/chart/events.cpp | 4 ++-- 15 files changed, 41 insertions(+), 55 deletions(-) diff --git a/src/base/alg/union_foreach.h b/src/base/alg/union_foreach.h index dbb444e5c..7d98574d5 100644 --- a/src/base/alg/union_foreach.h +++ b/src/base/alg/union_foreach.h @@ -1,11 +1,10 @@ #ifndef ALG_UNION_FOREACH_H #define ALG_UNION_FOREACH_H +#include #include #include -#include "union_foreach.h" - namespace Alg { @@ -192,8 +191,8 @@ template > Fun, Impl::Property Prop = Impl::default_property_t> -constexpr void union_foreach(R &&r1, - R &&r2, +constexpr void union_foreach(const R &r1, + const R &r2, Fun f, Comp comp = {}, Proj proj = {}, diff --git a/src/base/geom/point.h b/src/base/geom/point.h index 18d906554..e76c2cb5d 100644 --- a/src/base/geom/point.h +++ b/src/base/geom/point.h @@ -236,8 +236,9 @@ struct Point struct Size : Point { - [[nodiscard]] static Size - Coord(Orientation orientation, double value, double other = 0.0) + [[nodiscard]] static Size Oriented(Orientation orientation, + double value, + double other = 0.0) { return {Point::Coord(orientation, value, other)}; } diff --git a/src/base/geom/rect.cpp b/src/base/geom/rect.cpp index ae94afe1d..464da50cb 100644 --- a/src/base/geom/rect.cpp +++ b/src/base/geom/rect.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include "base/math/floating.h" diff --git a/src/chart/generator/axis.cpp b/src/chart/generator/axis.cpp index f22d0c168..02f0b3267 100644 --- a/src/chart/generator/axis.cpp +++ b/src/chart/generator/axis.cpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include #include diff --git a/src/chart/generator/marker.cpp b/src/chart/generator/marker.cpp index f3050df38..be3ce0b88 100644 --- a/src/chart/generator/marker.cpp +++ b/src/chart/generator/marker.cpp @@ -6,9 +6,7 @@ #include #include "base/conv/auto_json.h" -#include "base/geom/orientation.h" #include "base/geom/point.h" -#include "base/geom/rect.h" #include "base/math/range.h" #include "base/refl/auto_enum.h" #include "chart/options/align.h" diff --git a/src/chart/generator/plot.cpp b/src/chart/generator/plot.cpp index 619e700b1..02e78ec8c 100644 --- a/src/chart/generator/plot.cpp +++ b/src/chart/generator/plot.cpp @@ -9,7 +9,9 @@ #include #include "base/anim/interpolated.h" +#include "base/math/range.h" #include "chart/main/style.h" +#include "chart/options/channel.h" #include "chart/options/options.h" #include "marker.h" diff --git a/src/chart/generator/plotbuilder.cpp b/src/chart/generator/plotbuilder.cpp index dec00919b..7ed228c62 100644 --- a/src/chart/generator/plotbuilder.cpp +++ b/src/chart/generator/plotbuilder.cpp @@ -4,11 +4,12 @@ #include #include #include +#include #include -#include #include #include #include +#include #include #include #include diff --git a/src/chart/main/style.h b/src/chart/main/style.h index 77ec3221c..0cee95b4c 100644 --- a/src/chart/main/style.h +++ b/src/chart/main/style.h @@ -368,6 +368,11 @@ struct Plot : Padding, Box, PlotParams { return id == Gen::AxisId::x ? xAxis : yAxis; } + + [[nodiscard]] Axis &getAxis(Gen::AxisId id) + { + return id == Gen::AxisId::x ? xAxis : yAxis; + } }; struct LogoParams diff --git a/src/chart/main/stylesheet.cpp b/src/chart/main/stylesheet.cpp index 6d5481f04..d1d4abf14 100644 --- a/src/chart/main/stylesheet.cpp +++ b/src/chart/main/stylesheet.cpp @@ -92,7 +92,7 @@ void Sheet::setPlot() defaultParams.plot.paddingLeft = Gfx::Length::Emphemeral(45.0 / 12.0); } - else if (!options->isMeasure(+options->getVerticalChannel())) { + else if (!options->isMeasure(+!options->getHorizontalChannel())) { defaultParams.plot.paddingLeft = Gfx::Length::Emphemeral(80.0 / 12.0); } @@ -115,11 +115,10 @@ void Sheet::setAxisLabels() def.position = AxisLabel::Position::max_edge; def.side = AxisLabel::Side::positive; } - else if (!options->isMeasure(Gen::ChannelId::x) + else if (!options->isMeasure(+options->getHorizontalChannel()) && options->getChannels() - .at(Gen::AxisId::x) - .hasDimension() - && options->angle == 0) + .at(+options->getHorizontalChannel()) + .hasDimension()) def.angle.reset(); } @@ -154,13 +153,14 @@ void Sheet::setMarkers() defaultParams.plot.marker.fillOpacity = 0.8; } else if (options->geometry == Gen::ShapeType::rectangle) { - auto vIsMeasure = - options->isMeasure(+options->getVerticalChannel()); - auto hIsMeasure = - options->isMeasure(+options->getHorizontalChannel()); + auto vIsMeasure = options->isMeasure(Gen::ChannelId::y); + auto hIsMeasure = options->isMeasure(Gen::ChannelId::x); if (auto polar = options->coordSystem.get() == Gen::CoordSystem::polar; - polar && options->getVerticalAxis().isEmpty()) + polar + && options->getChannels() + .at(Gen::ChannelId::y) + .isEmpty()) defaultParams.plot.marker.rectangleSpacing = 0; else if (auto needRectangleSpacing = vIsMeasure != hIsMeasure @@ -221,7 +221,11 @@ void Sheet::setAfterStyles(Gen::Plot &plot, const Geom::Size &size) auto &style = plot.getStyle(); style.setup(); - if (auto &xLabel = style.plot.xAxis.label; !xLabel.angle) { + if (auto &xLabel = + style.plot + .getAxis(plot.getOptions()->getHorizontalChannel()) + .label; + !xLabel.angle) { auto plotX = size.x; auto em = style.calculatedSize(); diff --git a/src/chart/options/options.cpp b/src/chart/options/options.cpp index e1954ca8f..1d3fdaab2 100644 --- a/src/chart/options/options.cpp +++ b/src/chart/options/options.cpp @@ -8,6 +8,7 @@ #include "base/geom/orientation.h" #include "base/math/trig.h" +#include "base/refl/auto_enum.h" #include "dataframe/old/types.h" #include "channel.h" @@ -218,11 +219,6 @@ AxisId Options::getHorizontalChannel() const return Math::rad2quadrant(angle) % 2 == 0 ? AxisId::x : AxisId::y; } -AxisId Options::getVerticalChannel() const -{ - return !getHorizontalChannel(); -} - bool Options::isShapeValid(const ShapeType &shapeType) const { if (mainAxis().hasDimension()) return true; @@ -299,10 +295,10 @@ std::optional Options::getAutoLegend() const void Options::setAutoRange(bool hPositive, bool vPositive) { - auto &v = getVerticalAxis(); - auto &h = getHorizontalAxis(); - auto vHasMeasure = getVerticalAxis().hasMeasure(); - auto hHasMeasure = getHorizontalAxis().hasMeasure(); + auto &v = getChannels().at(AxisId::y); + auto &h = getChannels().at(AxisId::x); + auto vHasMeasure = v.hasMeasure(); + auto hHasMeasure = h.hasMeasure(); auto &&cart = coordSystem.get() == CoordSystem::cartesian; auto &&nrect = geometry != ShapeType::rectangle; diff --git a/src/chart/options/options.h b/src/chart/options/options.h index c18abfca2..29abea81a 100644 --- a/src/chart/options/options.h +++ b/src/chart/options/options.h @@ -161,27 +161,6 @@ class Options : public OptionProperties void simplify(); [[nodiscard]] AxisId getHorizontalChannel() const; - [[nodiscard]] AxisId getVerticalChannel() const; - - [[nodiscard]] const Channel &getHorizontalAxis() const - { - return channels.at(getHorizontalChannel()); - } - - [[nodiscard]] const Channel &getVerticalAxis() const - { - return channels.at(getVerticalChannel()); - } - - Channel &getHorizontalAxis() - { - return channels.at(getHorizontalChannel()); - } - - Channel &getVerticalAxis() - { - return channels.at(getVerticalChannel()); - } [[nodiscard]] bool isShapeValid(const ShapeType &) const; [[nodiscard]] std::optional getMarkerInfoId( diff --git a/src/chart/rendering/drawaxes.cpp b/src/chart/rendering/drawaxes.cpp index 0ea10c919..73092d80b 100644 --- a/src/chart/rendering/drawaxes.cpp +++ b/src/chart/rendering/drawaxes.cpp @@ -22,6 +22,7 @@ #include "base/math/renard.h" #include "base/refl/auto_enum.h" #include "base/type/booliter.h" +#include "chart/generator/axis.h" #include "chart/generator/plot.h" // NOLINT(misc-include-cleaner) #include "chart/main/events.h" #include "chart/main/style.h" diff --git a/src/chart/rendering/drawguides.cpp b/src/chart/rendering/drawguides.cpp index 1b0488118..c8c3c1a7c 100644 --- a/src/chart/rendering/drawguides.cpp +++ b/src/chart/rendering/drawguides.cpp @@ -2,6 +2,7 @@ #include +#include "base/geom/affinetransform.h" #include "base/geom/line.h" #include "base/geom/point.h" #include "base/math/fuzzybool.h" diff --git a/src/chart/rendering/drawinterlacing.cpp b/src/chart/rendering/drawinterlacing.cpp index d7a2d2986..f1b6553bd 100644 --- a/src/chart/rendering/drawinterlacing.cpp +++ b/src/chart/rendering/drawinterlacing.cpp @@ -8,6 +8,7 @@ #include #include "base/anim/interpolated.h" +#include "base/geom/affinetransform.h" #include "base/geom/point.h" #include "base/geom/rect.h" #include "base/gfx/colortransform.h" @@ -59,7 +60,7 @@ void DrawInterlacing::drawGeometries(Gen::AxisId axisIndex, return Geom::Rect{tr(Geom::Point::Coord(orientation, clippedBottom, from)), - tr(Geom::Size::Coord(orientation, + tr(Geom::Size::Oriented(orientation, clippedSize, to - from))}; }; diff --git a/test/unit/chart/events.cpp b/test/unit/chart/events.cpp index ebbd335b5..33e812308 100644 --- a/test/unit/chart/events.cpp +++ b/test/unit/chart/events.cpp @@ -434,8 +434,8 @@ const static auto tests = using Axis = Vizzu::Events::Targets::Axis; for (auto &&[beg, end] = events.equal_range("plot-axis-draw"); const auto &[j, t, l] : values(subrange(beg, end))) - if (!isHorizontal( - orientation(static_cast(*t).axis))) + if (static_cast(*t).axis + == Vizzu::Gen::AxisId::y) xCenter = std::get(l).line.begin.x; std::set zero_count{}; From 4694ee8ad67fa55330b749a70a4b519b43ca1203 Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Fri, 6 Dec 2024 19:10:41 +0100 Subject: [PATCH 11/11] Fix testcases --- src/chart/rendering/drawaxes.cpp | 3 ++- src/dataframe/impl/dataframe.cpp | 2 -- src/dataframe/old/datatable.cpp | 1 + test/e2e/test_cases/test_cases.json | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/chart/rendering/drawaxes.cpp b/src/chart/rendering/drawaxes.cpp index 73092d80b..efb88f3b0 100644 --- a/src/chart/rendering/drawaxes.cpp +++ b/src/chart/rendering/drawaxes.cpp @@ -105,7 +105,8 @@ const DrawAxes &&DrawAxes::init() && for (auto axisIndex : Refl::enum_values()) { const auto &axis = plot->axises.at(axisIndex); - const static Gen::SplitAxis::Parts oneSized{{}}; + const static Gen::SplitAxis::Parts oneSized{ + {std::size_t{}, Gen::SplitAxis::Part{}}}; splits[axisIndex] = axis.parts.empty() ? oneSized : axis.parts; diff --git a/src/dataframe/impl/dataframe.cpp b/src/dataframe/impl/dataframe.cpp index 48c95178b..c321a9310 100644 --- a/src/dataframe/impl/dataframe.cpp +++ b/src/dataframe/impl/dataframe.cpp @@ -8,12 +8,10 @@ #include #include #include -#include #include #include #include #include -#include #include #include #include diff --git a/src/dataframe/old/datatable.cpp b/src/dataframe/old/datatable.cpp index d3baca7cf..18da63923 100644 --- a/src/dataframe/old/datatable.cpp +++ b/src/dataframe/old/datatable.cpp @@ -16,6 +16,7 @@ #include "base/conv/auto_json.h" #include "base/conv/numtostr.h" +#include "base/conv/tostring.h" #include "base/refl/auto_enum.h" #include "base/text/smartstring.h" #include "chart/options/options.h" diff --git a/test/e2e/test_cases/test_cases.json b/test/e2e/test_cases/test_cases.json index 9885d08a2..cb3d47e63 100644 --- a/test/e2e/test_cases/test_cases.json +++ b/test/e2e/test_cases/test_cases.json @@ -53,7 +53,7 @@ "refs": ["cf62886"] }, "basic_animations/labels/rectangle_labels_rotated_charts": { - "refs": ["b79fb18"] + "refs": ["4b7c849"] }, "basic_animations/legend_transitions/color_2discrete_anim": { "refs": ["fed1ebe"] @@ -2441,7 +2441,7 @@ "refs": ["ecb5f2b"] }, "ww_noFade/wNoFade_Tests/Marker_label_problem/rotated_bar_to_donut": { - "refs": ["9196915"] + "refs": ["7f75c64"] }, "ww_noFade/wNoFade_Tests/Marker_transition_problem/Bubble_Stacked_Bubble_to_Area": { "refs": ["295cd11"]