From ae8de2ff8e72b246795b9a4cf39e26e09d0d4a36 Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Thu, 30 Jan 2025 16:27:11 +0100 Subject: [PATCH 01/23] nan on empty string (cherry picked from commit 510c08c7af21cd6990ca5fe6bf4a18b6b4c0b460) --- CHANGELOG.md | 4 +++- src/chart/options/options.cpp | 13 +++++++++++++ src/dataframe/impl/dataframe.cpp | 9 ++++++--- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb1e43122..4528f8fd2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,9 @@ - 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 count negative values too. +- Visible non-sum aggregated value jumped. +- Add record didn't handle when a measure got an empty string. ## [0.16.0] - 2024-11-28 diff --git a/src/chart/options/options.cpp b/src/chart/options/options.cpp index 42a1aae94..4687a1b80 100644 --- a/src/chart/options/options.cpp +++ b/src/chart/options/options.cpp @@ -162,6 +162,19 @@ void Options::simplify() // remove all dimensions, only used at the end of stack auto &stackChannel = this->stackChannel(); + if (auto &&meas = stackChannel.measure()) + if (meas->getAggr() != dataframe::aggregator_type::sum) + return; + + if (auto &&meas = this->mainAxis().measure()) + if (meas->getAggr() != dataframe::aggregator_type::sum) + return; + + if (auto &&leg = this->legend.get()) + if (auto &&meas = this->channels.at(*leg).measure()) + if (meas->getAggr() != dataframe::aggregator_type::sum) + return; + auto dimensions = stackChannel.dimensions(); auto copy = getChannels(); diff --git a/src/dataframe/impl/dataframe.cpp b/src/dataframe/impl/dataframe.cpp index feece15d4..93b9d7db6 100644 --- a/src/dataframe/impl/dataframe.cpp +++ b/src/dataframe/impl/dataframe.cpp @@ -411,7 +411,8 @@ void dataframe::add_record(std::span values) & auto &s = *unsafe_get(source); s.normalize_sizes(); - std::vector measures(s.measure_names.size()); + std::vector measures(s.measure_names.size(), + std::numeric_limits::quiet_NaN()); std::vector dimensions( s.dimension_names.size()); for (const auto *it = values.data(); const auto &col : *vec) { @@ -425,8 +426,10 @@ void dataframe::add_record(std::span values) & break; case measure: char *eof{}; - measures[&unsafe_get(ser).second - - s.measures.data()] = std::strtod(*it, &eof); + if (**it != '\0') + measures[&unsafe_get(ser).second + - s.measures.data()] = + std::strtod(*it, &eof); if (eof == *it) error(error_type::nan, *it); break; } From a1144268b20e0c0124eb60d8f3b416758e923627 Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Mon, 3 Feb 2025 12:28:59 +0100 Subject: [PATCH 02/23] Fix fontparent setter bug (cherry picked from commit a4b532b84ad754d721269169a24d37d10f7ad7b5) --- CHANGELOG.md | 1 + src/chart/animator/keyframe.cpp | 2 ++ src/chart/generator/plot.cpp | 4 +++- src/chart/main/chart.cpp | 5 ++++- src/chart/main/style.cpp | 2 +- src/chart/main/style.h | 2 +- src/chart/main/stylesheet.cpp | 2 +- src/chart/main/stylesheet.h | 7 ++++++- 8 files changed, 19 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4528f8fd2..dc21af008 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - Split chart count negative values too. - Visible non-sum aggregated value jumped. - Add record didn't handle when a measure got an empty string. +- Fix no fontparent set bug. ## [0.16.0] - 2024-11-28 diff --git a/src/chart/animator/keyframe.cpp b/src/chart/animator/keyframe.cpp index 182a52fe6..89c24dca6 100644 --- a/src/chart/animator/keyframe.cpp +++ b/src/chart/animator/keyframe.cpp @@ -71,6 +71,7 @@ void Keyframe::prepareActual() prepareActualMarkersInfo(); actual = std::make_shared(*source); + actual->getStyle().setup(); actual->detachOptions(); } @@ -92,6 +93,7 @@ void Keyframe::copyTarget() if (!targetCopy) { targetCopy = target; target = std::make_shared(*targetCopy); + target->getStyle().setup(); target->detachOptions(); } } diff --git a/src/chart/generator/plot.cpp b/src/chart/generator/plot.cpp index 98ac17d2c..86ad3fa91 100644 --- a/src/chart/generator/plot.cpp +++ b/src/chart/generator/plot.cpp @@ -56,7 +56,9 @@ Plot::Plot(PlotOptionsPtr opts, Styles::Chart style) : guides(*opts), options(std::move(opts)), style(std::move(style)) -{} +{ + this->style.setup(); +} void Plot::detachOptions() { diff --git a/src/chart/main/chart.cpp b/src/chart/main/chart.cpp index 054b52294..c0f80a5a4 100644 --- a/src/chart/main/chart.cpp +++ b/src/chart/main/chart.cpp @@ -29,6 +29,7 @@ Chart::Chart() : *events.animation.begin, *events.animation.complete) { + computedStyles.setup(); animator.onDraw.attach( [this](const Gen::PlotPtr &actPlot) { @@ -72,8 +73,9 @@ void Chart::animate(Anim::Animation::OnComplete &&onComplete) } else { *nextOptions = prevOptions; - actStyles = prevStyles; + setStyles(prevStyles); computedStyles = plot->getStyle(); + computedStyles.setup(); } }); @@ -132,6 +134,7 @@ Gen::PlotPtr Chart::plot(const Gen::PlotOptionsPtr &options) Styles::Sheet::setAfterStyles(*res, layout.boundary.size); computedStyles = res->getStyle(); + computedStyles.setup(); return res; } diff --git a/src/chart/main/style.cpp b/src/chart/main/style.cpp index 867a1d59e..1eae86419 100644 --- a/src/chart/main/style.cpp +++ b/src/chart/main/style.cpp @@ -584,7 +584,7 @@ struct FontParentSetter {} }; -void Chart::setup() +void Chart::setup() & { Refl::visit(FontParentSetter{this}, *this); fontParent = &getDefaultFont(); diff --git a/src/chart/main/style.h b/src/chart/main/style.h index 77ec3221c..efacb49b4 100644 --- a/src/chart/main/style.h +++ b/src/chart/main/style.h @@ -396,7 +396,7 @@ struct Chart : Padding, Box, Font, ChartParams static const Gfx::ColorPalette &getDefaultColorPalette(); static Chart def(); - void setup(); + void setup() &; }; } diff --git a/src/chart/main/stylesheet.cpp b/src/chart/main/stylesheet.cpp index 8faa306e1..4b4ef7e50 100644 --- a/src/chart/main/stylesheet.cpp +++ b/src/chart/main/stylesheet.cpp @@ -51,6 +51,7 @@ Chart Sheet::getFullParams(const Gen::PlotOptionsPtr &options, void Sheet::calcDefaults(const Geom::Size &size) { defaultParams = Chart::def(); + defaultParams.setup(); defaultParams.fontSize = Gfx::Length{baseFontSize(size, true)}; @@ -219,7 +220,6 @@ void Sheet::setData() 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) { auto plotX = size.x; diff --git a/src/chart/main/stylesheet.h b/src/chart/main/stylesheet.h index cc19cfdae..4b1931ac8 100644 --- a/src/chart/main/stylesheet.h +++ b/src/chart/main/stylesheet.h @@ -27,7 +27,12 @@ class Sheet : public Style::Sheet { public: using Base = Style::Sheet; - using Base::Sheet; + + Sheet(Chart &&defaultParams, Chart &activeParams) : + Base(std::move(defaultParams), activeParams) + { + this->defaultParams.setup(); + } Chart getFullParams(const Gen::PlotOptionsPtr &options, const Geom::Size &size); From cff830153afdd002f0d0916b7dbe3e193474dbf0 Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Fri, 21 Feb 2025 11:30:25 +0100 Subject: [PATCH 03/23] Fix ticket hash --- test/e2e/tests/tickets.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/tests/tickets.json b/test/e2e/tests/tickets.json index 58c9db713..aaf3ee839 100644 --- a/test/e2e/tests/tickets.json +++ b/test/e2e/tests/tickets.json @@ -17,7 +17,7 @@ "refs": ["29b4a25"] }, "268": { - "refs": ["af2ddae"] + "refs": ["491501f"] }, "300": { "refs": ["604d28a"] From d189db7d75de5dd8fb699bb7199ae637f56d595e Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Mon, 24 Feb 2025 15:41:39 +0100 Subject: [PATCH 04/23] fix nan handling --- CHANGELOG.md | 1 + src/base/math/renard.cpp | 8 ++++---- src/base/math/renard.h | 4 ++-- src/chart/generator/plotbuilder.cpp | 8 +++++--- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc21af008..1dbb020a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - Visible non-sum aggregated value jumped. - Add record didn't handle when a measure got an empty string. - Fix no fontparent set bug. +- Fix NaN handling on markers linking. ## [0.16.0] - 2024-11-28 diff --git a/src/base/math/renard.cpp b/src/base/math/renard.cpp index 6d6b64764..9dc484855 100644 --- a/src/base/math/renard.cpp +++ b/src/base/math/renard.cpp @@ -13,17 +13,17 @@ namespace Math Renard Renard::R3() { - static const std::array R3Numbers = {1.0, 2.0, 5.0, 10.0}; + static constexpr std::array R3Numbers = {1.0, 2.0, 5.0, 10.0}; return Renard{R3Numbers}; } Renard Renard::R5() { - static const std::array R5Numbers = + static constexpr std::array R5Numbers = {1.0, 1.5, 2.5, 4.0, 6.0, 10.0}; return Renard{R5Numbers}; } -double Renard::ceil(double value) +double Renard::ceil(double value) const { if (value == 0.0) return 0.0; @@ -39,7 +39,7 @@ double Renard::ceil(double value) + std::to_string(value) + "."); } -double Renard::floor(double value) +double Renard::floor(double value) const { if (value == 0.0) return 0.0; diff --git a/src/base/math/renard.h b/src/base/math/renard.h index 2d494ef98..481deb1c2 100644 --- a/src/base/math/renard.h +++ b/src/base/math/renard.h @@ -14,8 +14,8 @@ class Renard explicit Renard(std::span const &numbers) : numbers(numbers) {} - double ceil(double value); - double floor(double value); + [[nodiscard]] double ceil(double value) const; + [[nodiscard]] double floor(double value) const; private: std::span numbers; diff --git a/src/chart/generator/plotbuilder.cpp b/src/chart/generator/plotbuilder.cpp index 97f33988a..78cf992b1 100644 --- a/src/chart/generator/plotbuilder.cpp +++ b/src/chart/generator/plotbuilder.cpp @@ -275,9 +275,11 @@ bool PlotBuilder::linkMarkers(const Buckets &buckets, : *it.base().base().base(); if (act) - prevPos = - act->position.getCoord(orientation(axisIndex)) += + if (auto &&ppos = act->position.getCoord( + orientation(axisIndex)) += isAggregatable ? dimOffset[i] : prevPos; + std::isfinite(ppos)) + prevPos = ppos; hasConnection |= Marker::connectMarkers(iNext == 0 && act != next, @@ -303,7 +305,7 @@ void PlotBuilder::calcAxises(const Data::DataTable &dataTable) 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})); + stats.setIfRange(AxisId::y, yrange.getRange({0.0, 0.0})); } else { auto boundRect = markerIt->toRectangle().positive(); From 4e580856d8aa27ea4a0678e93cfdaa02308739ac Mon Sep 17 00:00:00 2001 From: David Vegh Date: Mon, 24 Feb 2025 16:19:56 +0100 Subject: [PATCH 05/23] Set version to 0.16.1 --- CHANGELOG.md | 2 ++ src/chart/main/version.cpp | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1dbb020a9..1de1851d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## [Unreleased] +## [0.16.1] - 2025-02-24 + ### Fixed - Fix invalid read/write when animation is contiguous (onFinish callback calls setKeyframe). diff --git a/src/chart/main/version.cpp b/src/chart/main/version.cpp index 92ff16a63..9b9d82e29 100644 --- a/src/chart/main/version.cpp +++ b/src/chart/main/version.cpp @@ -2,6 +2,6 @@ #include "base/app/version.h" -const App::Version Vizzu::Main::version(0, 16, 0); +const App::Version Vizzu::Main::version(0, 16, 1); const char *const Vizzu::Main::siteUrl = "https://vizzu.io/"; From abea9f4b3ad79172a86fc94e64ba36dec48f0a12 Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Mon, 24 Feb 2025 19:33:09 +0100 Subject: [PATCH 06/23] nan on empty string (cherry picked from commit 510c08c7af21cd6990ca5fe6bf4a18b6b4c0b460) --- CHANGELOG.md | 2 ++ src/chart/generator/marker.cpp | 3 ++- src/chart/generator/plotbuilder.cpp | 28 +++++++++++++++++++++------- test/e2e/test_cases/test_cases.json | 16 ++++++++-------- test/e2e/tests/tickets.json | 2 +- test/e2e/tests/tickets/142.mjs | 6 ++++-- 6 files changed, 38 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1de1851d4..d73a38715 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ - Add record didn't handle when a measure got an empty string. - Fix no fontparent set bug. - Fix NaN handling on markers linking. +- Fix handling negative numbers on stacked charts. +- Fix connected charts when nan values is the prev connection. ## [0.16.0] - 2024-11-28 diff --git a/src/chart/generator/marker.cpp b/src/chart/generator/marker.cpp index 87047be9c..00e03f4e0 100644 --- a/src/chart/generator/marker.cpp +++ b/src/chart/generator/marker.cpp @@ -152,7 +152,8 @@ bool Marker::connectMarkers(bool first, bool main, bool polarConnection) { - if (prev && next && main && (!first || polarConnection)) { + if (prev && next && main && (!first || polarConnection) + && prev->enabled) { next->prevMainMarker = RelativeMarkerIndex{prev->idx, prev - next}; next->polarConnection = polarConnection && first; diff --git a/src/chart/generator/plotbuilder.cpp b/src/chart/generator/plotbuilder.cpp index 78cf992b1..f2cceb4a6 100644 --- a/src/chart/generator/plotbuilder.cpp +++ b/src/chart/generator/plotbuilder.cpp @@ -212,6 +212,14 @@ bool PlotBuilder::linkMarkers(const Buckets &buckets, && plot->getOptions()->geometry.get() == ShapeType::rectangle); + auto &&subAxisDims = plot->getOptions()->subAxis().dimensions(); + auto isSubAggregatable = + !isMain + && plot->getOptions()->geometry.get() == ShapeType::rectangle + && !plot->getOptions()->mainAxis().dimensions().contains_any( + subAxisDims.begin(), + subAxisDims.end()); + if (isAggregatable) { double pre_neg{}; double pre_pos{}; @@ -252,7 +260,7 @@ bool PlotBuilder::linkMarkers(const Buckets &buckets, && plot->getOptions()->isHorizontal(); for (const auto &bucket : buckets) { - double prevPos{}; + std::array prevPositions{}; for (auto i = 0U; i < sorted.size(); ++i) { auto idAct = sorted[i].index; auto &&ids = std::ranges::views::values(bucket); @@ -274,12 +282,18 @@ bool PlotBuilder::linkMarkers(const Buckets &buckets, ? nullptr : *it.base().base().base(); - if (act) - if (auto &&ppos = act->position.getCoord( - orientation(axisIndex)) += - isAggregatable ? dimOffset[i] : prevPos; - std::isfinite(ppos)) - prevPos = ppos; + if (act) { + auto &apos = + act->position.getCoord(orientation(axisIndex)); + if (!std::isfinite(apos)) apos = 0; + if (isAggregatable) + apos += dimOffset[i]; + else { + auto &pp = prevPositions[isSubAggregatable + && std::signbit(apos)]; + pp = apos += pp; + } + } hasConnection |= Marker::connectMarkers(iNext == 0 && act != next, diff --git a/test/e2e/test_cases/test_cases.json b/test/e2e/test_cases/test_cases.json index 431a79d05..7200b9c0d 100644 --- a/test/e2e/test_cases/test_cases.json +++ b/test/e2e/test_cases/test_cases.json @@ -329,7 +329,7 @@ "refs": ["25a0806"] }, "static_chart_types/cartesian_coo_sys/bar_stacked_rectangle_negative_2dis_1con": { - "refs": ["1d13f90"] + "refs": ["db217d4"] }, "static_chart_types/cartesian_coo_sys/column_grouped_rectangle_negative_2dis_1con": { "refs": ["40427ee"] @@ -338,10 +338,10 @@ "refs": ["2fc95d7"] }, "static_chart_types/cartesian_coo_sys/column_stacked_rectangle_negative_2dis_1con": { - "refs": ["ca0c025"] + "refs": ["0eeb2ea"] }, "static_chart_types/cartesian_coo_sys/column_stacked_rectangle_negative_3dis_1con": { - "refs": ["9f9a668"] + "refs": ["2ae6815"] }, "static_chart_types/cartesian_coo_sys/dotplot_circle_negative_1dis_1con": { "refs": ["c6f3fae"] @@ -2234,7 +2234,7 @@ "refs": ["8f5ad3e"] }, "ww_noFade/wNoFade_Tests/1_des_pol/rectangle/03_rec": { - "refs": ["126b678"] + "refs": ["fa0c503"] }, "ww_noFade/wNoFade_Tests/1_des_pol/rectangle/04a_rec_1c": { "refs": ["6648873"] @@ -2258,13 +2258,13 @@ "refs": ["4119df4"] }, "ww_noFade/wNoFade_Tests/1_des_pol/rectangle/06a_rec_2c": { - "refs": ["8b3f74c"] + "refs": ["d5366ef"] }, "ww_noFade/wNoFade_Tests/1_des_pol/rectangle/06b_rec_1c": { "refs": ["dd722fc"] }, "ww_noFade/wNoFade_Tests/1_des_pol/rectangle/06b_rec_2c": { - "refs": ["1446959"] + "refs": ["e6630e1"] }, "ww_noFade/wNoFade_Tests/1_des_pol/rectangle/07a_rec_1c": { "refs": ["8468503"] @@ -2597,13 +2597,13 @@ "refs": ["e7c3c26"] }, "ww_noFade/wNoFade_cases/1_des_pol/rectangle/06a_rec_2c": { - "refs": ["8eb7f9b"] + "refs": ["3c9dd4d"] }, "ww_noFade/wNoFade_cases/1_des_pol/rectangle/06b_rec_1c": { "refs": ["4098464"] }, "ww_noFade/wNoFade_cases/1_des_pol/rectangle/06b_rec_2c": { - "refs": ["88106ed"] + "refs": ["b48d282"] }, "ww_noFade/wNoFade_cases/1_des_pol/rectangle/07a_rec_1c": { "refs": ["5d434f6"] diff --git a/test/e2e/tests/tickets.json b/test/e2e/tests/tickets.json index aaf3ee839..82b264147 100644 --- a/test/e2e/tests/tickets.json +++ b/test/e2e/tests/tickets.json @@ -5,7 +5,7 @@ "refs": ["9a881ef"] }, "142": { - "refs": ["a9a62c3"] + "refs": ["14f1d1f"] }, "145": { "refs": ["1928a0a"] diff --git a/test/e2e/tests/tickets/142.mjs b/test/e2e/tests/tickets/142.mjs index a32755a8b..a903aa9d8 100644 --- a/test/e2e/tests/tickets/142.mjs +++ b/test/e2e/tests/tickets/142.mjs @@ -16,13 +16,15 @@ const testSteps = [ (chart) => chart.animate({ y: ['Y0', 'm'], - x: ['X', 'm0'] + x: ['X', 'm0'], + color: { set: 'Y0', title: 'Y0' }, + label: ['X', 'm'] }), (chart) => chart.animate({ y: ['Y1', 'm'], x: ['X', 'm1'], - color: 'Y1' + color: { set: 'Y1', title: 'Y1' } }) ] From cc7b1aabb9f555ed2518570659fbbdd36f915215 Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Mon, 24 Feb 2025 19:41:07 +0100 Subject: [PATCH 07/23] fix ut --- test/unit/chart/events.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/chart/events.cpp b/test/unit/chart/events.cpp index ebbd335b5..05d476dae 100644 --- a/test/unit/chart/events.cpp +++ b/test/unit/chart/events.cpp @@ -427,7 +427,7 @@ const static auto tests = auto &&events = get_events(chart); check->*events.count("plot-axis-draw") == 1u; - check->*events.count("plot-axis-label-draw") == 3u + 3u; + check->*events.count("plot-axis-label-draw") == 4u + 4u; check->*events.count("plot-marker-draw") == 10u; double xCenter{}; From 2220d60248dbbbd2e51271e822e50e35b6b5adb5 Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Mon, 24 Feb 2025 19:52:46 +0100 Subject: [PATCH 08/23] clang-tidy --- src/chart/generator/marker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/chart/generator/marker.cpp b/src/chart/generator/marker.cpp index 00e03f4e0..0c3fb64c5 100644 --- a/src/chart/generator/marker.cpp +++ b/src/chart/generator/marker.cpp @@ -153,7 +153,7 @@ bool Marker::connectMarkers(bool first, bool polarConnection) { if (prev && next && main && (!first || polarConnection) - && prev->enabled) { + && static_cast(prev->enabled)) { next->prevMainMarker = RelativeMarkerIndex{prev->idx, prev - next}; next->polarConnection = polarConnection && first; From 029f939cc5766acc678f328f8c3814ebf6ddf3d6 Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Mon, 24 Feb 2025 20:13:40 +0100 Subject: [PATCH 09/23] clang-tidy --- src/chart/generator/plotbuilder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/chart/generator/plotbuilder.cpp b/src/chart/generator/plotbuilder.cpp index f2cceb4a6..9029b76f3 100644 --- a/src/chart/generator/plotbuilder.cpp +++ b/src/chart/generator/plotbuilder.cpp @@ -218,7 +218,7 @@ bool PlotBuilder::linkMarkers(const Buckets &buckets, && plot->getOptions()->geometry.get() == ShapeType::rectangle && !plot->getOptions()->mainAxis().dimensions().contains_any( subAxisDims.begin(), - subAxisDims.end()); + {}); if (isAggregatable) { double pre_neg{}; From 611da41bc642ac55a21f1164bed06e7c00bc01b9 Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Tue, 1 Apr 2025 16:31:21 +0200 Subject: [PATCH 10/23] remove combinedSizeOf vector-reserve usage --- src/chart/generator/axis.h | 6 ++- src/chart/generator/plotbuilder.cpp | 80 +++++++++++++++++------------ 2 files changed, 50 insertions(+), 36 deletions(-) diff --git a/src/chart/generator/axis.h b/src/chart/generator/axis.h index a982833de..7152f6271 100644 --- a/src/chart/generator/axis.h +++ b/src/chart/generator/axis.h @@ -21,7 +21,7 @@ namespace Vizzu::Gen struct ChannelStats { using TrackType = std::variant, - std::vector>>; + std::map>; Refl::EnumArray tracked; Math::Range<> lightness; @@ -35,7 +35,9 @@ struct ChannelStats void track(ChannelId at, const Data::MarkerId &id) { auto &vec = std::get<1>(tracked[at]); - vec[id.itemId] = id.label; + if (id.label) + vec.try_emplace(static_cast(id.itemId), + *id.label); } void track(ChannelId at, const double &value) diff --git a/src/chart/generator/plotbuilder.cpp b/src/chart/generator/plotbuilder.cpp index 9029b76f3..3e19eed3b 100644 --- a/src/chart/generator/plotbuilder.cpp +++ b/src/chart/generator/plotbuilder.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -77,8 +78,7 @@ void PlotBuilder::initDimensionTrackers() for (auto type : Refl::enum_values()) if (auto &&ch = plot->getOptions()->getChannels().at(type); !ch.hasMeasure()) - stats.tracked.at(type).emplace<1>( - dataCube.combinedSizeOf(ch.dimensions()).second); + stats.tracked.at(type).emplace<1>(); } Buckets PlotBuilder::generateMarkers(std::size_t &mainBucketSize) @@ -378,31 +378,30 @@ void PlotBuilder::calcLegendAndLabel(const Data::DataTable &dataTable) } } else if (!scale.isEmpty()) { - const auto &indices = std::get<1>(stats.at(type)); auto merge = type == LegendId::size || (type == LegendId::lightness && plot->getOptions()->dimLabelIndex(+type) == 0); - for (std::uint32_t i{}, count{}; i < indices.size(); ++i) - if (const auto &sliceIndex = indices[i]) { - auto rangeId = static_cast(i); - std::optional color; - if (type == LegendId::color) - color = ColorBase(i, 0.5); - else if (type == LegendId::lightness) { - rangeId = stats.lightness.rescale(rangeId); - color = ColorBase(0U, rangeId); - } - - if (calcLegend.dimension.add(*sliceIndex, - {rangeId, rangeId}, - count, - color, - true, - merge)) - ++count; + for (std::uint32_t count{}; const auto &[i, label] : + std::get<1>(stats.at(type))) { + auto rangeId = static_cast(i); + std::optional color; + if (type == LegendId::color) + color = ColorBase(i, 0.5); + else if (type == LegendId::lightness) { + rangeId = stats.lightness.rescale(rangeId); + color = ColorBase(0U, rangeId); } + if (calcLegend.dimension.add(label, + {rangeId, rangeId}, + count, + color, + true, + merge)) + ++count; + } + if (auto &&series = plot->getOptions()->labelSeries(type); series && isAutoTitle && calcLegend.dimension.empty()) calcLegend.title = series.value().getColIndex(); @@ -524,38 +523,51 @@ void PlotBuilder::addSeparation(const Buckets &subBuckets, auto align = plot->getOptions()->align; - std::vector ranges{mainBucketSize, Math::Range<>{{}, {}}}; - std::vector anyEnabled(mainBucketSize); + std::map> theRanges; auto &&subAxis = plot->getOptions()->subAxisType(); + constexpr Math::Range defRange{{}, {}}; for (auto &&bucket : subBuckets) for (std::size_t i{}, prIx{}; auto &&[marker, idx] : bucket) { if (!marker.enabled) continue; (i += idx.itemId - std::exchange(prIx, idx.itemId)) %= - ranges.size(); - ranges[i].include(marker.getSizeBy(subAxis).size()); - anyEnabled[i] = true; + mainBucketSize; + theRanges.try_emplace(i, defRange) + .first->second.include( + marker.getSizeBy(subAxis).size()); } - auto max = Math::Range<>{{}, {}}; - for (auto i = 0U; i < ranges.size(); ++i) - if (anyEnabled[i]) max = max + ranges[i]; + auto &&views = std::ranges::views::values(theRanges); + auto max = std::accumulate(views.begin(), views.end(), defRange); auto splitSpace = plot->getStyle() .plot.getAxis(plot->getOptions()->subAxisType()) .spacing->get(max.max, plot->getStyle().calculatedSize()); - for (auto i = 1U; i < ranges.size(); ++i) - ranges[i] = ranges[i] + ranges[i - 1].max - + (anyEnabled[i - 1] ? splitSpace : 0); + std::adjacent_difference(views.begin(), + views.end(), + std::next(views.begin()), + [&splitSpace](const auto &lhs, const auto &rhs) + { + return rhs + lhs.max + splitSpace; + }); for (auto &&bucket : subBuckets) for (std::size_t i{}, prIx{}; auto &&[marker, idx] : bucket) { (i += idx.itemId - std::exchange(prIx, idx.itemId)) %= - ranges.size(); + mainBucketSize; + + auto range = defRange; + auto upper = theRanges.upper_bound(i); + if (upper != theRanges.begin()) { + --upper; + range = upper->second; + if (upper->first != i) range.min = range.max; + } + marker.setSizeBy(subAxis, - Base::Align{align, ranges[i]}.getAligned( + Base::Align{align, range}.getAligned( marker.getSizeBy(subAxis))); } } From 21e362d9322779a1c1e5cb107aa5124763994faf Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Tue, 1 Apr 2025 16:46:46 +0200 Subject: [PATCH 11/23] clang-tidy --- CHANGELOG.md | 8 +++++++- src/chart/generator/plotbuilder.cpp | 13 ++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d73a38715..5689524e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ # Changelog -## [Unreleased] +## [Unreleased - 0.16.2] - 2025-04-?? + +### Fixed + +- Fix "Cerror: vector" where if a lot of categories are present without usage, + memory allocation could be failed. +- Fix appearing split markers position ## [0.16.1] - 2025-02-24 diff --git a/src/chart/generator/plotbuilder.cpp b/src/chart/generator/plotbuilder.cpp index 3e19eed3b..05b8863d1 100644 --- a/src/chart/generator/plotbuilder.cpp +++ b/src/chart/generator/plotbuilder.cpp @@ -526,7 +526,7 @@ void PlotBuilder::addSeparation(const Buckets &subBuckets, std::map> theRanges; auto &&subAxis = plot->getOptions()->subAxisType(); - constexpr Math::Range defRange{{}, {}}; + constexpr auto defRange = Math::Range<>{{}, {}}; for (auto &&bucket : subBuckets) for (std::size_t i{}, prIx{}; auto &&[marker, idx] : bucket) { if (!marker.enabled) continue; @@ -559,12 +559,11 @@ void PlotBuilder::addSeparation(const Buckets &subBuckets, mainBucketSize; auto range = defRange; - auto upper = theRanges.upper_bound(i); - if (upper != theRanges.begin()) { - --upper; - range = upper->second; - if (upper->first != i) range.min = range.max; - } + if (auto prevOrExact = theRanges.upper_bound(i); + prevOrExact != theRanges.begin()) + if (range = (--prevOrExact)->second; + prevOrExact->first != i) + range.min = range.max -= splitSpace / 2; marker.setSizeBy(subAxis, Base::Align{align, range}.getAligned( From 14d5e6b8b70db95397e7bc5540bdfc697f9e8aac Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Tue, 1 Apr 2025 17:59:59 +0200 Subject: [PATCH 12/23] clang-tidy --- CHANGELOG.md | 2 +- src/chart/generator/plotbuilder.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5689524e8..0e926bb58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ - Fix "Cerror: vector" where if a lot of categories are present without usage, memory allocation could be failed. -- Fix appearing split markers position +- Fix appearing split markers starter position to center of separation space. ## [0.16.1] - 2025-02-24 diff --git a/src/chart/generator/plotbuilder.cpp b/src/chart/generator/plotbuilder.cpp index 05b8863d1..cbe16f906 100644 --- a/src/chart/generator/plotbuilder.cpp +++ b/src/chart/generator/plotbuilder.cpp @@ -545,9 +545,9 @@ void PlotBuilder::addSeparation(const Buckets &subBuckets, .plot.getAxis(plot->getOptions()->subAxisType()) .spacing->get(max.max, plot->getStyle().calculatedSize()); - std::adjacent_difference(views.begin(), + std::partial_sum(views.begin(), views.end(), - std::next(views.begin()), + views.begin(), [&splitSpace](const auto &lhs, const auto &rhs) { return rhs + lhs.max + splitSpace; From b521a46cd9043acb55428e20d47fd0feceb0b022 Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Tue, 1 Apr 2025 21:56:10 +0200 Subject: [PATCH 13/23] Update plotbuilder.cpp --- src/chart/generator/plotbuilder.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/chart/generator/plotbuilder.cpp b/src/chart/generator/plotbuilder.cpp index cbe16f906..d7eb14be6 100644 --- a/src/chart/generator/plotbuilder.cpp +++ b/src/chart/generator/plotbuilder.cpp @@ -563,7 +563,7 @@ void PlotBuilder::addSeparation(const Buckets &subBuckets, prevOrExact != theRanges.begin()) if (range = (--prevOrExact)->second; prevOrExact->first != i) - range.min = range.max -= splitSpace / 2; + range.min = range.max += splitSpace / 2; marker.setSizeBy(subAxis, Base::Align{align, range}.getAligned( @@ -652,4 +652,4 @@ void PlotBuilder::normalizeColors() } } -} \ No newline at end of file +} From 3c3f8de3a3c33384b7cda13806ef7ab2d8784994 Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Wed, 2 Apr 2025 13:55:37 +0200 Subject: [PATCH 14/23] add test + self-review --- src/chart/generator/plotbuilder.cpp | 9 +--- test/e2e/tests/features.json | 3 ++ .../data_input/big_category_indexed.mjs | 54 +++++++++++++++++++ 3 files changed, 59 insertions(+), 7 deletions(-) create mode 100644 test/e2e/tests/features/data_input/big_category_indexed.mjs diff --git a/src/chart/generator/plotbuilder.cpp b/src/chart/generator/plotbuilder.cpp index d7eb14be6..172358f84 100644 --- a/src/chart/generator/plotbuilder.cpp +++ b/src/chart/generator/plotbuilder.cpp @@ -545,13 +545,8 @@ void PlotBuilder::addSeparation(const Buckets &subBuckets, .plot.getAxis(plot->getOptions()->subAxisType()) .spacing->get(max.max, plot->getStyle().calculatedSize()); - std::partial_sum(views.begin(), - views.end(), - views.begin(), - [&splitSpace](const auto &lhs, const auto &rhs) - { - return rhs + lhs.max + splitSpace; - }); + for (auto range = defRange; auto &v : views) + range = v = v + range.max + splitSpace; for (auto &&bucket : subBuckets) for (std::size_t i{}, prIx{}; auto &&[marker, idx] : bucket) { diff --git a/test/e2e/tests/features.json b/test/e2e/tests/features.json index b345352da..8b73623d7 100644 --- a/test/e2e/tests/features.json +++ b/test/e2e/tests/features.json @@ -36,6 +36,9 @@ }, "legend_interpolation": { "refs": ["0e7b2e8"] + }, + "data_input/big_category_indexed": { + "refs": ["fabc101"] } } } diff --git a/test/e2e/tests/features/data_input/big_category_indexed.mjs b/test/e2e/tests/features/data_input/big_category_indexed.mjs new file mode 100644 index 000000000..432bf523d --- /dev/null +++ b/test/e2e/tests/features/data_input/big_category_indexed.mjs @@ -0,0 +1,54 @@ +const data = { + series: [ + { + name: 'Dim1', + categories: Array.from(new Array(10000).keys()).map((a) => a + ''), + values: [1, 1, 3, 3, 5, 5, 7, 7] + }, + { + name: 'Dim2', + categories: Array.from(new Array(10000).keys()).map((a) => a + ''), + values: [0, 1, 0, 1, 0, 1, 0, 1], + type: 'dimension' + }, + { + name: 'Meas', + type: 'measure', + values: [1, 2, 3, 4, 5, 6, 7, 8] + } + ] +} + +const testSteps = [ + (chart) => chart.animate({ data }), + (chart) => + chart.animate({ + data: { + filter: (record) => record.Dim1 !== '5' + }, + config: { + x: ['Dim1', 'Dim2'], + y: 'Meas', + color: 'Dim2', + split: true + } + }), + (chart) => + chart.animate( + { + data: { + filter: () => true + }, + config: { + x: 'Dim2', + y: ['Dim1', 'Meas'], + color: 'Dim2' + } + }, + { + show: { delay: 0 } + } + ) +] + +export default testSteps From 60edada44a5378d5108f37aee5f734c7217b7dd4 Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Wed, 2 Apr 2025 14:21:09 +0200 Subject: [PATCH 15/23] revert self-review --- src/chart/generator/plotbuilder.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/chart/generator/plotbuilder.cpp b/src/chart/generator/plotbuilder.cpp index 172358f84..d7eb14be6 100644 --- a/src/chart/generator/plotbuilder.cpp +++ b/src/chart/generator/plotbuilder.cpp @@ -545,8 +545,13 @@ void PlotBuilder::addSeparation(const Buckets &subBuckets, .plot.getAxis(plot->getOptions()->subAxisType()) .spacing->get(max.max, plot->getStyle().calculatedSize()); - for (auto range = defRange; auto &v : views) - range = v = v + range.max + splitSpace; + std::partial_sum(views.begin(), + views.end(), + views.begin(), + [&splitSpace](const auto &lhs, const auto &rhs) + { + return rhs + lhs.max + splitSpace; + }); for (auto &&bucket : subBuckets) for (std::size_t i{}, prIx{}; auto &&[marker, idx] : bucket) { From 243985aff6842f002cdb3d32ab8c1892bbe1a79a Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Thu, 3 Apr 2025 10:37:52 +0200 Subject: [PATCH 16/23] add test + self-review --- test/e2e/tests/features.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/tests/features.json b/test/e2e/tests/features.json index 8b73623d7..9a9c55b4a 100644 --- a/test/e2e/tests/features.json +++ b/test/e2e/tests/features.json @@ -38,7 +38,7 @@ "refs": ["0e7b2e8"] }, "data_input/big_category_indexed": { - "refs": ["fabc101"] + "refs": ["6bcc489"] } } } From c0f921628d6471f96c5cb94554aa5a9c3619b86f Mon Sep 17 00:00:00 2001 From: David Vegh Date: Thu, 3 Apr 2025 11:18:48 +0200 Subject: [PATCH 17/23] fix 3d chart test --- CHANGELOG.md | 4 +++- .../test_cases/web_content/cookbook/rendering/3d_chart.mjs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e926bb58..f5bfbe29e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Changelog -## [Unreleased - 0.16.2] - 2025-04-?? +## [Unreleased] + +## [0.16.2] - 2025-04-03 ### Fixed diff --git a/test/e2e/test_cases/web_content/cookbook/rendering/3d_chart.mjs b/test/e2e/test_cases/web_content/cookbook/rendering/3d_chart.mjs index a0d5822a7..7054ddc37 100644 --- a/test/e2e/test_cases/web_content/cookbook/rendering/3d_chart.mjs +++ b/test/e2e/test_cases/web_content/cookbook/rendering/3d_chart.mjs @@ -3,7 +3,7 @@ import { data_6 } from '../../../../test_data/chart_types_eu.mjs' const testSteps = [ async (chart) => { await import('https://unpkg.com/tinycolor2@1.6.0/dist/tinycolor-min.js') - const THREE = await import('https://unpkg.com/three/build/three.module.js') + const THREE = await import('https://unpkg.com/three@0.132.1/build/three.module.js') const toCanvasRect = (rect) => { const coordSystem = chart.feature.coordSystem From c2fe9d28baf1da25892cf5c13245f44d3cfae461 Mon Sep 17 00:00:00 2001 From: David Vegh Date: Thu, 3 Apr 2025 11:38:13 +0200 Subject: [PATCH 18/23] change tinycolor, three cdn --- .../test_cases/web_content/cookbook/rendering/3d_chart.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/e2e/test_cases/web_content/cookbook/rendering/3d_chart.mjs b/test/e2e/test_cases/web_content/cookbook/rendering/3d_chart.mjs index 7054ddc37..59220eae8 100644 --- a/test/e2e/test_cases/web_content/cookbook/rendering/3d_chart.mjs +++ b/test/e2e/test_cases/web_content/cookbook/rendering/3d_chart.mjs @@ -2,8 +2,8 @@ import { data_6 } from '../../../../test_data/chart_types_eu.mjs' const testSteps = [ async (chart) => { - await import('https://unpkg.com/tinycolor2@1.6.0/dist/tinycolor-min.js') - const THREE = await import('https://unpkg.com/three@0.132.1/build/three.module.js') + await import('https://cdn.jsdelivr.net/npm/tinycolor2@1.6.0/dist/tinycolor-min.js') + const THREE = await import('https://cdn.jsdelivr.net/npm/three@0.175.0/build/three.module.js') const toCanvasRect = (rect) => { const coordSystem = chart.feature.coordSystem From cc7a07b3db58dd238871e781c5a850ab2d11a870 Mon Sep 17 00:00:00 2001 From: David Vegh Date: Thu, 3 Apr 2025 11:42:52 +0200 Subject: [PATCH 19/23] fix format --- .../test_cases/web_content/cookbook/rendering/3d_chart.mjs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/e2e/test_cases/web_content/cookbook/rendering/3d_chart.mjs b/test/e2e/test_cases/web_content/cookbook/rendering/3d_chart.mjs index 59220eae8..36e97854a 100644 --- a/test/e2e/test_cases/web_content/cookbook/rendering/3d_chart.mjs +++ b/test/e2e/test_cases/web_content/cookbook/rendering/3d_chart.mjs @@ -3,7 +3,9 @@ import { data_6 } from '../../../../test_data/chart_types_eu.mjs' const testSteps = [ async (chart) => { await import('https://cdn.jsdelivr.net/npm/tinycolor2@1.6.0/dist/tinycolor-min.js') - const THREE = await import('https://cdn.jsdelivr.net/npm/three@0.175.0/build/three.module.js') + const THREE = await import( + 'https://cdn.jsdelivr.net/npm/three@0.175.0/build/three.module.js' + ) const toCanvasRect = (rect) => { const coordSystem = chart.feature.coordSystem From f8d8d6943118ec12b0798b457db4bd00598c0533 Mon Sep 17 00:00:00 2001 From: David Vegh Date: Thu, 3 Apr 2025 11:58:40 +0200 Subject: [PATCH 20/23] Set version to 0.16.2 --- src/chart/main/version.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/chart/main/version.cpp b/src/chart/main/version.cpp index 9b9d82e29..72031a076 100644 --- a/src/chart/main/version.cpp +++ b/src/chart/main/version.cpp @@ -2,6 +2,6 @@ #include "base/app/version.h" -const App::Version Vizzu::Main::version(0, 16, 1); +const App::Version Vizzu::Main::version(0, 16, 2); const char *const Vizzu::Main::siteUrl = "https://vizzu.io/"; From df52b51b8fc7cb8c3954aa16da1d96474cf4b451 Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Fri, 25 Apr 2025 15:00:26 +0200 Subject: [PATCH 21/23] add % implementation + test --- src/chart/animator/styles.cpp | 3 +- src/chart/animator/styles.h | 3 +- src/chart/generator/plotbuilder.cpp | 40 ++++++- src/chart/main/style.cpp | 3 +- src/chart/main/style.h | 2 + test/e2e/tests/style_tests.json | 3 + .../style_tests/plot/markerLabelUnit.mjs | 106 ++++++++++++++++++ 7 files changed, 151 insertions(+), 9 deletions(-) create mode 100644 test/e2e/tests/style_tests/plot/markerLabelUnit.mjs diff --git a/src/chart/animator/styles.cpp b/src/chart/animator/styles.cpp index 2ea20a947..5b15e55fa 100644 --- a/src/chart/animator/styles.cpp +++ b/src/chart/animator/styles.cpp @@ -62,7 +62,8 @@ void StyleMorphFactory::operator()(const T &source, template requires(std::is_same_v || std::is_same_v - || std::is_same_v) + || std::is_same_v + || std::is_same_v) void StyleMorphFactory::operator()(const T &, const T &, T &) const {} diff --git a/src/chart/animator/styles.h b/src/chart/animator/styles.h index 07757ca96..3821fca47 100644 --- a/src/chart/animator/styles.h +++ b/src/chart/animator/styles.h @@ -84,7 +84,8 @@ class StyleMorphFactory template > requires(std::is_same_v || std::is_same_v - || std::is_same_v) + || std::is_same_v + || std::is_same_v) void operator()(const T &, const T &, T &) const; private: diff --git a/src/chart/generator/plotbuilder.cpp b/src/chart/generator/plotbuilder.cpp index d7eb14be6..b8280b588 100644 --- a/src/chart/generator/plotbuilder.cpp +++ b/src/chart/generator/plotbuilder.cpp @@ -411,11 +411,22 @@ void PlotBuilder::calcLegendAndLabel(const Data::DataTable &dataTable) if (auto &&meas = plot->getOptions() ->getChannels() .at(ChannelId::label) - .measure()) + .measure()) { + auto markerLabelsUnitPercent = + plot->getStyle().plot.marker.label.unit + == Styles::MarkerLabel::Unit::percent + && plot->getOptions()->align == Base::Align::Type::stretch + && plot->getOptions()->labelSeries( + plot->getOptions()->subAxisType()) + == *meas + && !plot->getOptions()->isSplit(); plot->axises.label = { - ::Anim::String{ - std::string{dataTable.getUnit(meas->getColIndex())}}, + ::Anim::String{std::string{ + markerLabelsUnitPercent + ? "%" + : dataTable.getUnit(meas->getColIndex())}}, ::Anim::String{meas->getColIndex()}}; + } } void PlotBuilder::calcAxis(const Data::DataTable &dataTable, @@ -500,6 +511,19 @@ void PlotBuilder::addAlignment(const Buckets &subBuckets) const } auto &&subAxis = plot->getOptions()->subAxisType(); + + auto &&subAxisLabel = plot->getOptions()->labelSeries(subAxis); + auto markerLabelsUnitPercent = + plot->getStyle().plot.marker.label.unit + == Styles::MarkerLabel::Unit::percent + && plot->getOptions()->align == Base::Align::Type::stretch + && subAxisLabel.has_value() + && subAxisLabel + == plot->getOptions() + ->getChannels() + .at(ChannelId::label) + .measure(); + const Base::Align align{plot->getOptions()->align, {0.0, 1.0}}; for (auto &&bucket : subBuckets) { Math::Range<> range; @@ -510,9 +534,13 @@ void PlotBuilder::addAlignment(const Buckets &subBuckets) const auto &&transform = align.getAligned(range) / range; - for (auto &&[marker, idx] : bucket) - marker.setSizeBy(subAxis, - marker.getSizeBy(subAxis) * transform); + for (auto &&[marker, idx] : bucket) { + auto &&newRange = marker.getSizeBy(subAxis) * transform; + marker.setSizeBy(subAxis, newRange); + if (markerLabelsUnitPercent) + marker.label->value.value.emplace( + newRange.size() * 100); + } } } diff --git a/src/chart/main/style.cpp b/src/chart/main/style.cpp index 1eae86419..af55752cb 100644 --- a/src/chart/main/style.cpp +++ b/src/chart/main/style.cpp @@ -185,7 +185,8 @@ Chart Chart::def() { .position = Anim::Interpolated(MarkerLabel::Position::center), .filter = Gfx::ColorTransform::Lightness(0), - .format = MarkerLabel::Format::measureFirst + .format = MarkerLabel::Format::measureFirst, + .unit = MarkerLabel::Unit::original } } } diff --git a/src/chart/main/style.h b/src/chart/main/style.h index efacb49b4..82971668e 100644 --- a/src/chart/main/style.h +++ b/src/chart/main/style.h @@ -261,10 +261,12 @@ struct MarkerLabelParams measureFirst, dimensionsFirst }; + enum class Unit : std::uint8_t { original, percent }; Param<::Anim::Interpolated> position; Param filter; Param format; + Param unit; }; struct MarkerLabel : OrientedLabel, MarkerLabelParams diff --git a/test/e2e/tests/style_tests.json b/test/e2e/tests/style_tests.json index cb820c8db..bb30086a8 100644 --- a/test/e2e/tests/style_tests.json +++ b/test/e2e/tests/style_tests.json @@ -1020,6 +1020,9 @@ }, "legend/offsetY": { "refs": ["b326287"] + }, + "plot/markerLabelUnit": { + "refs": ["ae0f5f5"] } } } diff --git a/test/e2e/tests/style_tests/plot/markerLabelUnit.mjs b/test/e2e/tests/style_tests/plot/markerLabelUnit.mjs new file mode 100644 index 000000000..4b5205069 --- /dev/null +++ b/test/e2e/tests/style_tests/plot/markerLabelUnit.mjs @@ -0,0 +1,106 @@ +const testSteps = [ + (chart) => { + const data = { + series: [ + { + name: 'Foo', + values: [ + 'A', + 'B', + 'C', + 'A', + 'B', + 'C', + 'A', + 'B', + 'C', + 'A', + 'B', + 'C', + 'A', + 'B', + 'C', + 'B', + 'B', + 'B', + 'B' + ] + }, + { + name: 'Foo2', + values: [ + '1', + '1', + '1', + '2', + '2', + '2', + '3', + '3', + '3', + '4', + '4', + '4', + '5', + '5', + '5', + '1', + '2', + '3', + '4' + ] + }, + { + name: 'Foo3', + values: [ + '0', + '1', + '0', + '2', + '3', + '2', + '0', + '1', + '0', + '2', + '3', + '2', + '0', + '1', + '0', + '4', + '4', + '4', + '4' + ] + }, + { + name: 'Bar', + values: [NaN, 1, NaN, 1, 2, 1, NaN, 1, NaN, 1, 2, 1, NaN, 1, NaN, 0, 0, 0, 0] + }, + { name: 'Bar2', values: [2, 2, 2, 1, 1, 1, 2, 2, 2, 1, 1, 1, 2, 2, 2, 4, 2, 4, 2] } + ] + } + + chart.feature('tooltip', true) + return chart.animate({ data }) + }, + (chart) => + chart.animate({ + config: { + x: ['Foo2'], + y: ['Bar2', 'Foo3'], + color: ['Foo3'], + label: ['Bar2'], + align: 'stretch' + } + }), + (chart) => + chart.animate({ + style: { + 'plot.marker.label.unit': 'percent' + } + }) +] + +export default testSteps From 70847e76b6bac4fb8f8f39a46c90f58c5db06b5a Mon Sep 17 00:00:00 2001 From: David Vegh Date: Mon, 28 Apr 2025 10:53:50 +0200 Subject: [PATCH 22/23] Set version to 0.16.3 --- CHANGELOG.md | 4 ++++ src/chart/main/version.cpp | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f5bfbe29e..4e1ca79a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +## [0.16.3] - 2025-04-28 + +- Added new (development) style parameter: 'plot.marker.label.unit' with options 'original' (default) and 'percent'. + ## [0.16.2] - 2025-04-03 ### Fixed diff --git a/src/chart/main/version.cpp b/src/chart/main/version.cpp index 72031a076..763e9e89f 100644 --- a/src/chart/main/version.cpp +++ b/src/chart/main/version.cpp @@ -2,6 +2,6 @@ #include "base/app/version.h" -const App::Version Vizzu::Main::version(0, 16, 2); +const App::Version Vizzu::Main::version(0, 16, 3); const char *const Vizzu::Main::siteUrl = "https://vizzu.io/"; From 1fdaee1bbb80bf1f193dee6ccd602148df5a69d0 Mon Sep 17 00:00:00 2001 From: David Vegh <61405792+veghdev@users.noreply.github.com> Date: Mon, 28 Apr 2025 11:20:11 +0200 Subject: [PATCH 23/23] Fix changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e1ca79a8..46bec1e9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ ## [0.16.3] - 2025-04-28 +### Added + - Added new (development) style parameter: 'plot.marker.label.unit' with options 'original' (default) and 'percent'. ## [0.16.2] - 2025-04-03