diff --git a/CHANGELOG.md b/CHANGELOG.md index cb1e43122..46bec1e9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,11 +2,33 @@ ## [Unreleased] +## [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 + +### Fixed + +- Fix "Cerror: vector" where if a lot of categories are present without usage, + memory allocation could be failed. +- Fix appearing split markers starter position to center of separation space. + +## [0.16.1] - 2025-02-24 + ### Fixed - 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. +- 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/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/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/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/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/marker.cpp b/src/chart/generator/marker.cpp index 87047be9c..0c3fb64c5 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) + && static_cast(prev->enabled)) { next->prevMainMarker = RelativeMarkerIndex{prev->idx, prev - next}; next->polarConnection = polarConnection && first; 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/generator/plotbuilder.cpp b/src/chart/generator/plotbuilder.cpp index 97f33988a..b8280b588 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) @@ -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(), + {}); + 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,10 +282,18 @@ bool PlotBuilder::linkMarkers(const Buckets &buckets, ? nullptr : *it.base().base().base(); - if (act) - prevPos = - act->position.getCoord(orientation(axisIndex)) += - isAggregatable ? dimOffset[i] : prevPos; + 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, @@ -303,7 +319,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(); @@ -362,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(); @@ -396,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, @@ -485,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; @@ -495,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); + } } } @@ -508,38 +551,50 @@ 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 auto defRange = Math::Range<>{{}, {}}; 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::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) { (i += idx.itemId - std::exchange(prIx, idx.itemId)) %= - ranges.size(); + mainBucketSize; + + auto range = defRange; + 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, ranges[i]}.getAligned( + Base::Align{align, range}.getAligned( marker.getSizeBy(subAxis))); } } @@ -625,4 +680,4 @@ void PlotBuilder::normalizeColors() } } -} \ No newline at end of file +} 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..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 } } } @@ -584,7 +585,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..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 @@ -396,7 +398,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); diff --git a/src/chart/main/version.cpp b/src/chart/main/version.cpp index 92ff16a63..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, 0); +const App::Version Vizzu::Main::version(0, 16, 3); const char *const Vizzu::Main::siteUrl = "https://vizzu.io/"; 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; } 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/test_cases/web_content/cookbook/rendering/3d_chart.mjs b/test/e2e/test_cases/web_content/cookbook/rendering/3d_chart.mjs index a0d5822a7..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 @@ -2,8 +2,10 @@ 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') + 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 diff --git a/test/e2e/tests/features.json b/test/e2e/tests/features.json index b345352da..9a9c55b4a 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": ["6bcc489"] } } } 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 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 diff --git a/test/e2e/tests/tickets.json b/test/e2e/tests/tickets.json index 58c9db713..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"] @@ -17,7 +17,7 @@ "refs": ["29b4a25"] }, "268": { - "refs": ["af2ddae"] + "refs": ["491501f"] }, "300": { "refs": ["604d28a"] 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' } }) ] 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{};