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/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/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/"; 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