From 2710ba443908a51b1cb4b28d8bee47b175d54b3f Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Mon, 10 Feb 2025 16:58:48 +0100 Subject: [PATCH 01/13] datatable interface --- src/chart/main/chart.cpp | 7 +- src/chart/main/chart.h | 2 +- src/dataframe/impl/data_source.h | 8 -- src/dataframe/impl/dataframe.cpp | 3 +- src/dataframe/impl/dataframe.h | 2 +- src/dataframe/interface.cpp | 11 +++ src/dataframe/interface.h | 13 +++ src/dataframe/old/datatable.cpp | 57 ++++---------- src/dataframe/old/datatable.h | 4 +- src/dataframe/old/types.h | 131 ++++++++++++++++++++++++++++++- 10 files changed, 176 insertions(+), 62 deletions(-) diff --git a/src/chart/main/chart.cpp b/src/chart/main/chart.cpp index c0f80a5a4..6966ab737 100644 --- a/src/chart/main/chart.cpp +++ b/src/chart/main/chart.cpp @@ -21,6 +21,7 @@ namespace Vizzu { Chart::Chart() : + table{dataframe::dataframe::create_new()}, nextOptions(std::make_shared()), stylesheet(Styles::Chart::def(), actStyles), computedStyles(stylesheet.getDefaultParams()), @@ -105,9 +106,9 @@ void Chart::draw(Gfx::ICanvas &canvas) { renderedChart = Draw::RenderedChart{ actPlot ? Draw::CoordinateSystem{layout.plotArea, - actPlot->getOptions()->angle, - actPlot->getOptions()->coordSystem, - actPlot->keepAspectRatio} + actPlot->getOptions()->angle, + actPlot->getOptions()->coordSystem, + actPlot->keepAspectRatio} : Draw::CoordinateSystem{layout.plotArea}, actPlot}; diff --git a/src/chart/main/chart.h b/src/chart/main/chart.h index 7d226b8c6..05fa73ec4 100644 --- a/src/chart/main/chart.h +++ b/src/chart/main/chart.h @@ -81,7 +81,7 @@ class Chart private: Layout layout; - Data::DataTable table; + Data::DataTableImpl table; Gen::PlotPtr actPlot; Gen::PlotOptionsPtr nextOptions; Gen::Options prevOptions; diff --git a/src/dataframe/impl/data_source.h b/src/dataframe/impl/data_source.h index fb2232637..f7d120ee0 100644 --- a/src/dataframe/impl/data_source.h +++ b/src/dataframe/impl/data_source.h @@ -15,8 +15,6 @@ namespace Vizzu::dataframe { -enum class series_type : std::uint8_t { unknown, dimension, measure }; - enum class error_type : std::uint8_t { series_not_found, duplicated_series, @@ -29,12 +27,6 @@ enum class error_type : std::uint8_t { internal_error }; -struct series_meta_t -{ - std::string name; - series_type type; -}; - [[maybe_unused]] [[noreturn]] void error(error_type err, std::string_view arg = {}); diff --git a/src/dataframe/impl/dataframe.cpp b/src/dataframe/impl/dataframe.cpp index 2be9551fa..1ca9339e3 100644 --- a/src/dataframe/impl/dataframe.cpp +++ b/src/dataframe/impl/dataframe.cpp @@ -723,7 +723,8 @@ std::string dataframe::get_record_id(std::size_t my_record) & return my_record < ids.size() ? ids[my_record] : std::string{}; } -series_meta_t dataframe::get_series_meta(const std::string &id) const +series_meta_t dataframe::get_series_meta( + const std::string &id) const & { switch (auto &&ser = get_data_source().get_series(id)) { using enum series_type; diff --git a/src/dataframe/impl/dataframe.h b/src/dataframe/impl/dataframe.h index aa60cfdfe..e61f46b6d 100644 --- a/src/dataframe/impl/dataframe.h +++ b/src/dataframe/impl/dataframe.h @@ -120,7 +120,7 @@ class dataframe const std::string_view &dimension) const &; [[nodiscard]] series_meta_t get_series_meta( - const std::string &id) const; + const std::string &id) const &; [[nodiscard]] std::string_view get_series_info( const std::string_view &id, diff --git a/src/dataframe/interface.cpp b/src/dataframe/interface.cpp index 64823e549..a57b5389c 100644 --- a/src/dataframe/interface.cpp +++ b/src/dataframe/interface.cpp @@ -176,6 +176,12 @@ std::size_t dataframe_interface::get_record_count() const & return as_impl(this).get_record_count(); } +series_meta_t dataframe_interface::get_series_meta( + const std::string &id) const & +{ + return as_impl(this).get_series_meta(id); +} + std::string_view dataframe_interface::get_series_info( const std::string_view &id, const char *key) const & @@ -201,4 +207,9 @@ std::string dataframe_interface::get_record_id( return as_impl(this).get_record_id(my_record); } +[[nodiscard]] std::string dataframe_interface::as_string() const & +{ + return as_impl(this).as_string(); +} + } \ No newline at end of file diff --git a/src/dataframe/interface.h b/src/dataframe/interface.h index 6c2233368..96af2fb24 100644 --- a/src/dataframe/interface.h +++ b/src/dataframe/interface.h @@ -21,6 +21,14 @@ namespace Vizzu::dataframe using cell_value = std::variant; using cell_reference = std::variant; +enum class series_type : std::uint8_t { unknown, dimension, measure }; + +struct series_meta_t +{ + std::string name; + series_type type{}; +}; + enum class aggregator_type : std::uint8_t { sum, min, @@ -154,6 +162,9 @@ class alignas(align_impl) dataframe_interface [[nodiscard]] std::size_t get_record_count() const &; + [[nodiscard]] series_meta_t get_series_meta( + const std::string &id) const &; + [[nodiscard]] std::string_view get_series_info( const std::string_view &id, const char *key) const &; @@ -166,6 +177,8 @@ class alignas(align_impl) dataframe_interface [[nodiscard]] std::string get_record_id(std::size_t my_record) &; + [[nodiscard]] std::string as_string() const &; + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,modernize-avoid-c-arrays) alignas(align_impl) std::byte data[max_size_impl]; }; diff --git a/src/dataframe/old/datatable.cpp b/src/dataframe/old/datatable.cpp index 007e66d53..eef5d1fd7 100644 --- a/src/dataframe/old/datatable.cpp +++ b/src/dataframe/old/datatable.cpp @@ -105,38 +105,16 @@ DataCube::DataCube(const DataTable &table, auto &&channels = options.getChannels(); auto &&dimensions = channels.getDimensions(); auto &&measures = channels.getMeasures(); - auto empty = dimensions.empty() && measures.empty(); - df = {empty ? dataframe::dataframe::create_new() - : table.copy(false)}; - - if (empty) { + if (dimensions.empty() && measures.empty()) { + df = dataframe::dataframe::create_new(); df->finalize(); return; } - df->remove_records(options.dataFilter.getFunction()); - - auto removed = df->copy(false); - - for (const auto &dim : dimensions) - df->aggregate_by(dim.getColIndex()); - - for (const auto &meas : measures) { - auto &&sid = meas.getColIndex(); - auto &&aggr = meas.getAggr(); - - measure_names.try_emplace(std::pair{sid, aggr}, - df->set_aggregate(sid, aggr)); - } - - for (const auto &dim : dimensions) { - df->set_sort(dim.getColIndex(), - dataframe::sort_type::less, - dataframe::na_position::first); - } + std::tie(df, measure_names) = + table.aggregate(options.dataFilter, dimensions, measures); - df->finalize(); for (std::size_t ix{}; const auto &dim : dimensions) { auto &&dimName = dim.getColIndex(); auto &&cats = df->get_categories(dimName); @@ -167,22 +145,16 @@ DataCube::DataCube(const DataTable &table, std::swap(sumBy, common); if (sumBy.empty()) continue; - auto &sub_df = - *cacheImpl.try_emplace(channelId, removed->copy(false)) - .first->second; - - auto &&set = sumBy.as_set(); - for (auto first = set.begin(), last = set.end(); - auto &&dim : dimensions) - if (first == last || dim < *first) - sub_df.aggregate_by(dim.getColIndex()); - else - ++first; - - std::ignore = sub_df.set_aggregate(meas->getColIndex(), - meas->getAggr()); + auto dimCp{dimensions}; + std::erase_if(dimCp, + [&sumBy](const auto &dim) + { + return sumBy.contains(dim); + }); - sub_df.finalize(); + cacheImpl.try_emplace(channelId, + table.aggregate(options.dataFilter, dimCp, {*meas}) + .first); } } @@ -209,8 +181,7 @@ bool DataCube::empty() const const std::string &DataCube::getName( const SeriesIndex &seriesId) const { - return measure_names.at( - {seriesId.getColIndex(), seriesId.getAggr()}); + return measure_names.at(seriesId); } MarkerId DataCube::getId(const SeriesList &sl, diff --git a/src/dataframe/old/datatable.h b/src/dataframe/old/datatable.h index e9747f6ea..eb8b6f807 100644 --- a/src/dataframe/old/datatable.h +++ b/src/dataframe/old/datatable.h @@ -25,9 +25,7 @@ class DataCube public: std::shared_ptr df; - std::map, - std::string> - measure_names; + std::map measure_names; struct DimensionInfo { diff --git a/src/dataframe/old/types.h b/src/dataframe/old/types.h index 8ae49cea5..a632d7ae7 100644 --- a/src/dataframe/old/types.h +++ b/src/dataframe/old/types.h @@ -15,8 +15,7 @@ class dataframe; namespace Vizzu::Data { - -using DataTable = dataframe::dataframe; +struct DataTable; class DataCube; struct RowWrapper @@ -164,6 +163,134 @@ struct MarkerId return itemId <=> id.itemId; } }; + +struct DataTable +{ + [[nodiscard]] virtual std::string_view get_series_info( + const std::string_view &id, + const char *key) const & = 0; + + [[nodiscard]] virtual dataframe::series_meta_t get_series_meta( + const std::string &id) const & = 0; + + virtual void add_dimension( + std::span dimension_categories, + std::span dimension_values, + std::string_view name, + std::span> info = + {}, + dataframe::adding_type adding_strategy = + dataframe::adding_type::create_or_override) & = 0; + + virtual void add_measure(std::span measure_values, + std::string_view name, + std::span> info = + {}, + dataframe::adding_type adding_strategy = + dataframe::adding_type::create_or_override) & = 0; + + virtual void add_record( + std::span values) & = 0; + + [[nodiscard]] virtual std::string as_string() const & = 0; + + [[nodiscard]] virtual std::pair< + std::shared_ptr, + std::map> + aggregate(const Filter &filter, + const std::set &aggregate_by, + const std::set &aggregating) const & = 0; + + virtual ~DataTable() = default; +}; + +struct DataTableImpl final : DataTable +{ + std::shared_ptr df; + + explicit DataTableImpl( + std::shared_ptr &&df) noexcept + : + df(std::move(df)) + {} + + [[nodiscard]] std::string_view get_series_info( + const std::string_view &id, + const char *key) const & override + { + return df->get_series_info(id, key); + } + + [[nodiscard]] dataframe::series_meta_t get_series_meta( + const std::string &id) const & override + { + return df->get_series_meta(id); + } + + void add_dimension( + std::span dimension_categories, + std::span dimension_values, + std::string_view name, + std::span> info, + dataframe::adding_type adding_strategy) + & override + { + df->add_dimension(dimension_categories, + dimension_values, + name, + info, + adding_strategy); + } + + void add_measure(std::span measure_values, + std::string_view name, + std::span> info, + dataframe::adding_type adding_strategy) + & override + { + df->add_measure(measure_values, name, info, adding_strategy); + } + + void add_record(std::span values) & override + { + df->add_record(values); + } + + [[nodiscard]] std::string as_string() const & override + { + return df->as_string(); + } + + [[nodiscard]] std::pair< + std::shared_ptr, + std::map> + aggregate(const Filter &filter, + const std::set &aggregate_by, + const std::set &aggregating) const & override + { + auto &&res = std::pair{df->copy(false), + std::map{}}; + + res.first->remove_records(filter.getFunction()); + + for (const auto &dim : aggregate_by) + res.first->aggregate_by(dim.getColIndex()); + + for (const auto &meas : aggregating) + res.second.try_emplace(meas, + res.first->set_aggregate(meas.getColIndex(), + meas.getAggr())); + + for (const auto &dim : aggregate_by) + res.first->set_sort(dim.getColIndex(), + dataframe::sort_type::less, + dataframe::na_position::first); + + res.first->finalize(); + return res; + } +}; + } #endif \ No newline at end of file From 7f45eacbc7aa49deadb5912044656f850702d899 Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Mon, 10 Feb 2025 17:19:27 +0100 Subject: [PATCH 02/13] move table to cinterface --- src/apps/weblib/cinterface.cpp | 2 +- src/apps/weblib/cinterface.h | 1 + src/apps/weblib/interface.cpp | 7 +++++-- src/apps/weblib/interface.h | 12 ++++++++++-- src/chart/main/chart.cpp | 10 +++++----- src/chart/main/chart.h | 6 +++--- src/chart/ui/chart.cpp | 4 +++- src/chart/ui/chart.h | 2 +- 8 files changed, 29 insertions(+), 15 deletions(-) diff --git a/src/apps/weblib/cinterface.cpp b/src/apps/weblib/cinterface.cpp index 6b15ae9f5..cb31abe23 100644 --- a/src/apps/weblib/cinterface.cpp +++ b/src/apps/weblib/cinterface.cpp @@ -125,7 +125,7 @@ void vizzu_setLogging(bool enable) { Interface::setLogging(enable); } APIHandles::Chart vizzu_createChart() { - return Interface::getInstance().createChart(); + return Interface::getInstance().createChart({}); } APIHandles::Canvas vizzu_createCanvas() diff --git a/src/apps/weblib/cinterface.h b/src/apps/weblib/cinterface.h index 1f0876d03..197f60a30 100644 --- a/src/apps/weblib/cinterface.h +++ b/src/apps/weblib/cinterface.h @@ -14,6 +14,7 @@ namespace APIHandles { using Any = const void *; using Chart = const void *; +using DataTable = const void *; using Snapshot = const void *; using Event = Util::EventDispatcher::Params *; using Animation = const void *; diff --git a/src/apps/weblib/interface.cpp b/src/apps/weblib/interface.cpp index 899f573bf..5468a937a 100644 --- a/src/apps/weblib/interface.cpp +++ b/src/apps/weblib/interface.cpp @@ -354,9 +354,12 @@ const char *Interface::dataMetaInfo(ObjectRegistryHandle chart) return res.c_str(); } -ObjectRegistryHandle Interface::createChart() +ObjectRegistryHandle Interface::createChart(ObjectRegistryHandle data) { - auto &&widget = std::make_shared(); + auto &&widget = std::make_shared( + data ? objects.get(data) + : std::make_shared( + dataframe::dataframe::create_new())); auto &openUrl = widget->openUrl; auto &doChange = widget->getChart().onChanged; diff --git a/src/apps/weblib/interface.h b/src/apps/weblib/interface.h index bda5f0760..285952d0d 100644 --- a/src/apps/weblib/interface.h +++ b/src/apps/weblib/interface.h @@ -12,6 +12,10 @@ struct ICanvas; namespace Vizzu { +namespace Data +{ +struct DataTable; +} namespace UI { class ChartWidget; @@ -26,7 +30,7 @@ class Interface Interface(); static const char *version(); - ObjectRegistryHandle createChart(); + ObjectRegistryHandle createChart(ObjectRegistryHandle data); ObjectRegistryHandle createCanvas(); static void setLogging(bool enable); void pointerMove(ObjectRegistryHandle chart, @@ -139,7 +143,11 @@ class Interface std::shared_ptr getChart(ObjectRegistryHandle chart); - ObjectRegistry + ObjectRegistry objects; }; diff --git a/src/chart/main/chart.cpp b/src/chart/main/chart.cpp index 6966ab737..789dee219 100644 --- a/src/chart/main/chart.cpp +++ b/src/chart/main/chart.cpp @@ -20,13 +20,13 @@ namespace Vizzu { -Chart::Chart() : - table{dataframe::dataframe::create_new()}, +Chart::Chart(const std::shared_ptr &table) : + table{table}, nextOptions(std::make_shared()), stylesheet(Styles::Chart::def(), actStyles), computedStyles(stylesheet.getDefaultParams()), events(eventDispatcher), - animator(table, + animator(*table, *events.animation.begin, *events.animation.complete) { @@ -99,7 +99,7 @@ void Chart::setAnimation(const Anim::AnimationPtr &animation) Gen::Config Chart::getConfig() { - return Gen::Config{getOptions(), table}; + return Gen::Config{getOptions(), *table}; } void Chart::draw(Gfx::ICanvas &canvas) @@ -127,7 +127,7 @@ Gen::PlotPtr Chart::plot(const Gen::PlotOptionsPtr &options) { options->setAutoParameters(); - auto res = Gen::PlotBuilder{table, + auto res = Gen::PlotBuilder{*table, options, stylesheet.getFullParams(options, layout.boundary.size)} .build(); diff --git a/src/chart/main/chart.h b/src/chart/main/chart.h index 05fa73ec4..3359029f1 100644 --- a/src/chart/main/chart.h +++ b/src/chart/main/chart.h @@ -28,12 +28,12 @@ class Chart public: Util::Event<> onChanged; - Chart(); + Chart(const std::shared_ptr &table); Chart(Chart &&) noexcept = delete; void draw(Gfx::ICanvas &canvas); void setBoundRect(const Geom::Rect &rect); - Data::DataTable &getTable() { return table; } + Data::DataTable &getTable() const { return *table; } Styles::Sheet &getStylesheet() { return stylesheet; } Styles::Chart &getStyles() { return actStyles; } [[nodiscard]] const Styles::Chart &getComputedStyles() const @@ -81,7 +81,7 @@ class Chart private: Layout layout; - Data::DataTableImpl table; + std::shared_ptr table; Gen::PlotPtr actPlot; Gen::PlotOptionsPtr nextOptions; Gen::Options prevOptions; diff --git a/src/chart/ui/chart.cpp b/src/chart/ui/chart.cpp index 1b5401c88..b75986088 100644 --- a/src/chart/ui/chart.cpp +++ b/src/chart/ui/chart.cpp @@ -12,7 +12,9 @@ namespace Vizzu::UI { -ChartWidget::ChartWidget() : +ChartWidget::ChartWidget( + const std::shared_ptr &table) : + chart(table), onClick(chart.getEventDispatcher().createEvent("click")), onPointerMoveEvent( chart.getEventDispatcher().createEvent("pointermove")), diff --git a/src/chart/ui/chart.h b/src/chart/ui/chart.h index 104da4f63..dab30b6f6 100644 --- a/src/chart/ui/chart.h +++ b/src/chart/ui/chart.h @@ -14,7 +14,7 @@ class ChartWidget public: Util::Event openUrl; - explicit ChartWidget(); + explicit ChartWidget(const std::shared_ptr &); void onPointerDown(const GUI::PointerEvent &event) const; void onPointerMove(const GUI::PointerEvent &event) const; From 169bb272af8871479721757b979adef1b7952b8c Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Wed, 12 Feb 2025 12:41:02 +0100 Subject: [PATCH 03/13] dummy ts --- project/cmake/weblib/emcc.txt | 2 + src/apps/weblib/cinterface.cpp | 31 +++- src/apps/weblib/cinterface.h | 19 ++- src/apps/weblib/interface.cpp | 199 +++++++++++++++++++++-- src/apps/weblib/interface.h | 41 ++++- src/apps/weblib/jsfunctionwrapper.h | 20 --- src/apps/weblib/jswrappers.h | 36 ++++ src/apps/weblib/ts-api/chart.ts | 6 +- src/apps/weblib/ts-api/cvizzu.types.d.ts | 11 +- src/apps/weblib/ts-api/data.ts | 9 +- src/apps/weblib/ts-api/module/cdata.ts | 4 +- src/apps/weblib/ts-api/module/module.ts | 15 +- src/dataframe/impl/dataframe.cpp | 3 +- src/dataframe/impl/dataframe.h | 5 +- src/dataframe/interface.cpp | 2 +- src/dataframe/interface.h | 5 +- src/dataframe/old/types.h | 13 +- test/unit/chart/events.cpp | 3 +- 18 files changed, 349 insertions(+), 75 deletions(-) delete mode 100644 src/apps/weblib/jsfunctionwrapper.h create mode 100644 src/apps/weblib/jswrappers.h diff --git a/project/cmake/weblib/emcc.txt b/project/cmake/weblib/emcc.txt index 1d0acdc34..8220c0425 100644 --- a/project/cmake/weblib/emcc.txt +++ b/project/cmake/weblib/emcc.txt @@ -11,6 +11,8 @@ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} \ -s EXPORTED_FUNCTIONS=[\ '_malloc',\ '_free',\ +'_vizzu_createData',\ +'_vizzu_createExternalData',\ '_vizzu_createChart',\ '_vizzu_createCanvas',\ '_vizzu_pointerDown',\ diff --git a/src/apps/weblib/cinterface.cpp b/src/apps/weblib/cinterface.cpp index cb31abe23..345b21139 100644 --- a/src/apps/weblib/cinterface.cpp +++ b/src/apps/weblib/cinterface.cpp @@ -123,9 +123,34 @@ const char *vizzu_version() { return Interface::version(); } void vizzu_setLogging(bool enable) { Interface::setLogging(enable); } -APIHandles::Chart vizzu_createChart() +APIHandles::DataTable vizzu_createData() { - return Interface::getInstance().createChart({}); + return Interface::getInstance().createData(); +} + +APIHandles::DataTable vizzu_createExternalData( + void (*stringDeleter)(const char *), + bool (*seriesMeta)(const char *), + const char *(*seriesInfo)(const char *, const char *), + void (*aggregator)(APIHandles::DataTable, + bool (*)(const Vizzu::Data::RowWrapper *), + bool (*)(const Vizzu::Data::RowWrapper *), + std::uint32_t, + const char *const *, + std::uint32_t, + const char *const *, + const char *const *, + const char **), + void (*deleter)()) +{ + return Interface::getInstance().createExternalData( + {{stringDeleter, seriesMeta, seriesInfo, aggregator}, + deleter}); +} + +APIHandles::Chart vizzu_createChart(APIHandles::DataTable data) +{ + return Interface::getInstance().createChart(data); } APIHandles::Canvas vizzu_createCanvas() @@ -295,7 +320,7 @@ const Value *record_getValue(const Vizzu::Data::RowWrapper *record, void data_addDimension(APIHandles::Chart chart, const char *name, - const char **categories, + const char *const *categories, std::uint32_t categoriesCount, const std::uint32_t *categoryIndices, std::uint32_t categoryIndicesCount, diff --git a/src/apps/weblib/cinterface.h b/src/apps/weblib/cinterface.h index 197f60a30..734639de0 100644 --- a/src/apps/weblib/cinterface.h +++ b/src/apps/weblib/cinterface.h @@ -40,7 +40,22 @@ struct alignas(double) Value }; }; -extern APIHandles::Chart vizzu_createChart(); +extern APIHandles::DataTable vizzu_createData(); +extern APIHandles::DataTable vizzu_createExternalData( + void (*stringDeleter)(const char *), + bool (*seriesMeta)(const char *), + const char *(*seriesInfo)(const char *, const char *), + void (*aggregator)(APIHandles::DataTable, + bool (*)(const Vizzu::Data::RowWrapper *), + bool (*)(const Vizzu::Data::RowWrapper *), + std::uint32_t, + const char *const *, + std::uint32_t, + const char *const *, + const char *const *, + const char **), + void (*deleter)()); +extern APIHandles::Chart vizzu_createChart(APIHandles::DataTable); extern APIHandles::Canvas vizzu_createCanvas(); extern void vizzu_pointerDown(APIHandles::Chart chart, APIHandles::Canvas canvas, @@ -80,7 +95,7 @@ extern const char *vizzu_version(); extern void data_addDimension(APIHandles::Chart chart, const char *name, - const char **categories, + const char *const *categories, std::uint32_t categoriesCount, const std::uint32_t *categoryIndices, std::uint32_t categoryIndicesCount, diff --git a/src/apps/weblib/interface.cpp b/src/apps/weblib/interface.cpp index 5468a937a..f5a44fc98 100644 --- a/src/apps/weblib/interface.cpp +++ b/src/apps/weblib/interface.cpp @@ -27,7 +27,7 @@ #include "cinterface.h" #include "interfacejs.h" #include "jscriptcanvas.h" -#include "jsfunctionwrapper.h" +#include "jswrappers.h" #include "objectregistry.h" namespace Vizzu @@ -216,6 +216,12 @@ std::variant Interface::getRecordValue( return record.get_value(column); } +std::shared_ptr Interface::getTable( + ObjectRegistryHandle data) +{ + return objects.get(data); +} + void Interface::addEventListener(ObjectRegistryHandle chart, const char *event, void (*callback)(APIHandles::Event, const char *)) @@ -314,52 +320,214 @@ void Interface::setAnimValue(ObjectRegistryHandle chart, getChart(chart)->getAnimOptions().set(path, value); } -void Interface::addDimension(ObjectRegistryHandle chart, +void Interface::addDimension(ObjectRegistryHandle table, const char *name, - const char **categories, + const char *const *categories, std::uint32_t categoriesCount, const std::uint32_t *categoryIndices, std::uint32_t categoryIndicesCount, bool isContiguous) { - getChart(chart)->getTable().add_dimension( - {categories, categoriesCount}, + getTable(table)->add_dimension({categories, categoriesCount}, {categoryIndices, categoryIndicesCount}, name, {{{"isContiguous", isContiguous ? "true" : "false"}}}); } -void Interface::addMeasure(ObjectRegistryHandle chart, +void Interface::addMeasure(ObjectRegistryHandle table, const char *name, const char *unit, const double *values, std::uint32_t count) { - getChart(chart)->getTable().add_measure({values, count}, + getTable(table)->add_measure({values, count}, name, {{std::pair{"unit", unit}}}); } -void Interface::addRecord(ObjectRegistryHandle chart, +void Interface::addRecord(ObjectRegistryHandle table, const char *const *cells, std::uint32_t count) { - getChart(chart)->getTable().add_record({cells, count}); + getTable(table)->add_record({cells, count}); } -const char *Interface::dataMetaInfo(ObjectRegistryHandle chart) +const char *Interface::dataMetaInfo(ObjectRegistryHandle table) { thread_local std::string res; - res = getChart(chart)->getTable().as_string(); + res = getTable(table)->as_string(); return res.c_str(); } +struct IntFDataTable final : Data::DataTable +{ + JsCompositionWrapper externalData; + + [[nodiscard]] explicit IntFDataTable( + JsCompositionWrapper + &&external_data) : + externalData(std::move(external_data)) + {} + + [[nodiscard]] dataframe::series_meta_t get_series_meta( + const std::string &id) const & override + { + return {id, + externalData.values.seriesMeta(id.c_str()) + ? dataframe::series_type::dimension + : dataframe::series_type::measure}; + } + + [[nodiscard]] std::string_view get_series_info( + const std::string &id, + const char *key) const & override + { + thread_local std::string res; + res = create_unique_ptr( + externalData.values.seriesInfo(id.c_str(), key), + externalData.values.stringDeleter) + .get(); + return res; + } + + [[noreturn]] static void unsupported() + { + throw std::logic_error("unsupported"); + } + + [[noreturn]] void add_dimension(std::span, + std::span, + std::string_view, + std::span>, + dataframe::adding_type) + & override + { + unsupported(); + } + + [[noreturn]] void add_measure(std::span, + std::string_view, + std::span>, + dataframe::adding_type) + & override + { + unsupported(); + } + + [[noreturn]] void add_record(std::span) + & override + { + unsupported(); + } + + [[nodiscard]] std::string as_string() const & override + { + unsupported(); + } + + [[nodiscard]] std::pair< + std::shared_ptr, + std::map> + aggregate(const Data::Filter &filter, + const std::set &aggregate_by, + const std::set &aggregating) + const & override + { + auto &intf = Interface::getInstance(); + auto &&res = dataframe::dataframe::create_new(); + auto &&obj = intf.createData(&res); + + std::vector aggregateBy(aggregate_by.size()); + std::ranges::transform(aggregate_by, + aggregateBy.begin(), + [](const Data::SeriesIndex &index) + { + return index.getColIndex().c_str(); + }); + + std::vector aggregatingCols(aggregating.size()); + std::ranges::transform(aggregating, + aggregatingCols.begin(), + [](const Data::SeriesIndex &index) + { + return index.getColIndex().c_str(); + }); + + using ArrType = + Refl::EnumArray; + static ArrType arr = std::apply( + [](auto &&...names) + { + return ArrType{{std::string{names}...}}; + }, + Refl::enum_names); + + std::vector aggregatingFun(aggregating.size()); + std::ranges::transform(aggregating, + aggregatingFun.begin(), + [](const Data::SeriesIndex &index) -> const char * + { + return arr[index.getAggr()].c_str(); + }); + + std::vector< + std::unique_ptr> + aggregatingNames; + aggregatingNames.reserve(aggregating.size()); + + { + std::vector aggregatingRawNames( + aggregating.size()); + + externalData.values.aggregator(obj, + filter.getFun1(), + filter.getFun2(), + aggregateBy.size(), + aggregateBy.data(), + aggregating.size(), + aggregatingCols.data(), + aggregatingFun.data(), + aggregatingRawNames.data()); + + for (auto &&ptr : std::move(aggregatingRawNames)) + aggregatingNames.emplace_back(ptr, + externalData.values.stringDeleter); + } + + intf.freeObj(obj); + + std::map resMap; + for (auto it = aggregatingNames.data(); + auto &&agg : aggregating) + if (auto &&ptr = *it++) + resMap[agg] = ptr.get(); + else + resMap[agg] = + Refl::enum_name(agg.getAggr()) + + " of " + agg.getColIndex(); + + return {res, resMap}; + } +}; + +ObjectRegistryHandle Interface::createData( + std::shared_ptr *df) +{ + return objects.reg(std::make_shared( + df ? *df : dataframe::dataframe::create_new())); +} + +ObjectRegistryHandle Interface::createExternalData( + JsCompositionWrapper &&externalData) +{ + return objects.reg( + std::make_shared(std::move(externalData))); +} + ObjectRegistryHandle Interface::createChart(ObjectRegistryHandle data) { auto &&widget = std::make_shared( - data ? objects.get(data) - : std::make_shared( - dataframe::dataframe::create_new())); + objects.get(data)); auto &openUrl = widget->openUrl; auto &doChange = widget->getChart().onChanged; @@ -383,8 +551,7 @@ ObjectRegistryHandle Interface::createChart(ObjectRegistryHandle data) ObjectRegistryHandle Interface::createCanvas() { - return objects.reg( - std::make_shared()); + return objects.reg(std::make_shared()); } void Interface::setLogging(bool enable) diff --git a/src/apps/weblib/interface.h b/src/apps/weblib/interface.h index 285952d0d..d4cddaca7 100644 --- a/src/apps/weblib/interface.h +++ b/src/apps/weblib/interface.h @@ -2,7 +2,7 @@ #define LIB_INTERFACE_H #include "cinterface.h" -#include "jsfunctionwrapper.h" +#include "jswrappers.h" #include "objectregistry.h" namespace Gfx @@ -12,6 +12,10 @@ struct ICanvas; namespace Vizzu { +namespace dataframe +{ +class alignas(alignof(double)) dataframe_interface; +} namespace Data { struct DataTable; @@ -26,10 +30,31 @@ class Chart; class Interface { public: + struct ExternalData + { + void (*stringDeleter)(const char *); + bool (*seriesMeta)(const char *); + const char *(*seriesInfo)(const char *, const char *); + void (*aggregator)(ObjectRegistryHandle, + bool (*)(const Data::RowWrapper *), + bool (*)(const Data::RowWrapper *), + std::uint32_t, + const char *const *, + std::uint32_t, + const char *const *, + const char *const *, + const char **); + }; + static Interface &getInstance(); Interface(); static const char *version(); + ObjectRegistryHandle createExternalData( + JsCompositionWrapper &&externalData); + ObjectRegistryHandle createData( + std::shared_ptr *df = + nullptr); ObjectRegistryHandle createChart(ObjectRegistryHandle data); ObjectRegistryHandle createCanvas(); static void setLogging(bool enable); @@ -99,22 +124,23 @@ class Interface double &rx, double &ry); - void addDimension(ObjectRegistryHandle chart, + void addDimension(ObjectRegistryHandle table, const char *name, - const char **categories, + const char *const *categories, std::uint32_t categoriesCount, const std::uint32_t *categoryIndices, std::uint32_t categoryIndicesCount, bool isContiguous); - void addMeasure(ObjectRegistryHandle chart, + void addMeasure(ObjectRegistryHandle table, const char *name, const char *unit, const double *values, std::uint32_t count); - void addRecord(ObjectRegistryHandle chart, + void addRecord(ObjectRegistryHandle table, const char *const *cells, std::uint32_t count); - const char *dataMetaInfo(ObjectRegistryHandle chart); + const char *dataMetaInfo(ObjectRegistryHandle table); + void addEventListener(ObjectRegistryHandle chart, const char *event, void (*callback)(APIHandles::Event, const char *)); @@ -137,6 +163,9 @@ class Interface const Data::RowWrapper &record, const char *column); + std::shared_ptr getTable( + ObjectRegistryHandle data); + private: struct Snapshot; struct Animation; diff --git a/src/apps/weblib/jsfunctionwrapper.h b/src/apps/weblib/jsfunctionwrapper.h deleted file mode 100644 index 19ba8890a..000000000 --- a/src/apps/weblib/jsfunctionwrapper.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef LIB_JSFUNCTIONWRAPPER_H -#define LIB_JSFUNCTIONWRAPPER_H - -#include - -#include "interfacejs.h" - -namespace Vizzu -{ - -// Unlike std::unique_ptr, the deleter of std::shared_ptr is invoked -// even if the managed pointer is null -> keep unique_ptr -template -using JsFunctionWrapper = - std::unique_ptr *...), - void (*)(R(std::remove_reference_t *...))>; - -} - -#endif diff --git a/src/apps/weblib/jswrappers.h b/src/apps/weblib/jswrappers.h new file mode 100644 index 000000000..e0c6adbe2 --- /dev/null +++ b/src/apps/weblib/jswrappers.h @@ -0,0 +1,36 @@ +#ifndef LIB_JSWRAPPERS_H +#define LIB_JSWRAPPERS_H + +#include + +namespace Vizzu +{ + +// Unlike std::unique_ptr, the deleter of std::shared_ptr is invoked +// even if the managed pointer is null -> keep unique_ptr +template +using JsFunctionWrapper = + std::unique_ptr *...), + void (*)(R(std::remove_reference_t *...))>; + +template struct JsCompositionWrapper +{ + T values; + void (*deleter)(); + ~JsCompositionWrapper() { deleter(); } + [[nodiscard]] JsCompositionWrapper(T &&values, + void (*deleter)()) : + values(std::move(values)), + deleter(deleter) + {} + JsCompositionWrapper(const JsCompositionWrapper &) = delete; + JsCompositionWrapper(JsCompositionWrapper &&) noexcept = default; + JsCompositionWrapper &operator=( + const JsCompositionWrapper &) = delete; + JsCompositionWrapper &operator=( + JsCompositionWrapper &&) noexcept = default; +}; + +} + +#endif diff --git a/src/apps/weblib/ts-api/chart.ts b/src/apps/weblib/ts-api/chart.ts index 532195ba9..98b577665 100644 --- a/src/apps/weblib/ts-api/chart.ts +++ b/src/apps/weblib/ts-api/chart.ts @@ -49,12 +49,12 @@ export class Chart implements ChartInterface { this._plugins = plugins this._module = module - this._cChart = this._module.createChart() + this._cData = this._module.createData() + this._cChart = this._module.createChart(this._cData) this._module.registerChart(this._cChart, this) this._cCanvas = this._module.createCanvas() - this._cData = this._module.getData(this._cChart) - this._data = new Data(this._cData) + this._data = new Data(this._cData, this._cChart.getId()) this._events = new Events(this._cChart) this._plugins.init(this._events) diff --git a/src/apps/weblib/ts-api/cvizzu.types.d.ts b/src/apps/weblib/ts-api/cvizzu.types.d.ts index a7d295b45..4b00f0fa6 100644 --- a/src/apps/weblib/ts-api/cvizzu.types.d.ts +++ b/src/apps/weblib/ts-api/cvizzu.types.d.ts @@ -7,6 +7,7 @@ export type CException = CPointer export type CTypeInfo = CPointer export type CSnapshotPtr = CPointer export type CAnimationPtr = CPointer +export type CDataPtr = CPointer export type CChartPtr = CPointer export type CCanvasPtr = CPointer export type CEventPtr = CPointer @@ -65,7 +66,15 @@ export interface CVizzu { ExceptionInfo: CExceptionInfoConstructor // exported functions - _vizzu_createChart(): CChartPtr + _vizzu_createData(): CDataPtr + _vizzu_createExternalData( + stringDeleter : CFunction, + seriesMeta : CFunction, + seriesInfo : CFunction, + aggregator : CFunction, + deleter : CFunction + ): CDataPtr + _vizzu_createChart(data : CDataPtr): CChartPtr _vizzu_createCanvas(): CCanvasPtr _vizzu_pointerDown( chart: CChartPtr, diff --git a/src/apps/weblib/ts-api/data.ts b/src/apps/weblib/ts-api/data.ts index 2da3963f8..fdbe719a2 100644 --- a/src/apps/weblib/ts-api/data.ts +++ b/src/apps/weblib/ts-api/data.ts @@ -3,13 +3,16 @@ import * as D from './types/data.js' import { CRecord, CData } from './module/cdata.js' import { DataRecord } from './datarecord.js' import { Mirrored } from './tsutils.js' +import { CPointer } from './cvizzu.types'; type DataTypes = D.DimensionValue | D.MeasureValue export class Data { private _cData: CData + private _cChart: CPointer - constructor(cData: CData) { + constructor(cData: CData, cChart: CPointer) { this._cData = cData + this._cChart = cChart } set(obj?: D.TableBySeries | D.TableByRecords): void { @@ -259,9 +262,9 @@ export class Data { private _setFilter(filter: D.FilterCallback | null): void { if (typeof filter === 'function') { const callback = (cRecord: CRecord): boolean => filter(new DataRecord(cRecord)) - this._cData.setFilter(callback) + this._cData.setFilter(callback, this._cChart) } else if (filter === null) { - this._cData.setFilter(null) + this._cData.setFilter(null, this._cChart) } else { throw new Error('data filter is not a function or null') } diff --git a/src/apps/weblib/ts-api/module/cdata.ts b/src/apps/weblib/ts-api/module/cdata.ts index c4876c293..f0089ba9b 100644 --- a/src/apps/weblib/ts-api/module/cdata.ts +++ b/src/apps/weblib/ts-api/module/cdata.ts @@ -128,7 +128,7 @@ export class CData extends CObject { } } - setFilter(callback: ((record: CRecord) => boolean) | null): void { + setFilter(callback: ((record: CRecord) => boolean) | null, chartId: CPointer): void { const callbackPtrs: [CPointer, CPointer] = [0, 0] if (callback !== null) { const f = (recordPtr: CRecordPtr): boolean => callback(new CRecord(this, recordPtr)) @@ -140,6 +140,6 @@ export class CData extends CObject { } callbackPtrs[1] = this._wasm.addFunction(deleter, 'vi') } - this._call(this._wasm._chart_setFilter)(...callbackPtrs) + this._callStatic(this._wasm._chart_setFilter).bind(this, chartId)(...callbackPtrs) } } diff --git a/src/apps/weblib/ts-api/module/module.ts b/src/apps/weblib/ts-api/module/module.ts index 326930f2e..d6ee64493 100644 --- a/src/apps/weblib/ts-api/module/module.ts +++ b/src/apps/weblib/ts-api/module/module.ts @@ -1,4 +1,4 @@ -import { CVizzu } from '../cvizzu.types' +import { CPointer, CVizzu } from '../cvizzu.types'; import { ObjectRegistry } from './objregistry.js' import { CEnv } from './cenv.js' @@ -45,10 +45,6 @@ export class Module extends CEnv { this._callStatic(this._wasm._vizzu_setLogging)(enabled) } - getData(cChart: CChart): CData { - return new CData(cChart.getId, this) - } - getCoordSystem(cChart: CChart): CCoordSystem { return new CCoordSystem(cChart.getId, this) } @@ -57,8 +53,13 @@ export class Module extends CEnv { return new CAnimControl(cChart.getId, this) } - createChart(): CChart { - return new CChart(this, this._getStatic(this._wasm._vizzu_createChart)) + createData(): CData { + return new CData(this._getStatic(this._wasm._vizzu_createData), this) + } + + createChart(data : CData): CChart { + const cPointer = this._callStatic(this._wasm._vizzu_createChart)(data.getId()) + return new CChart(this, (): CPointer => cPointer) } createCanvas(): CCanvas { diff --git a/src/dataframe/impl/dataframe.cpp b/src/dataframe/impl/dataframe.cpp index 1ca9339e3..e8e863a7f 100644 --- a/src/dataframe/impl/dataframe.cpp +++ b/src/dataframe/impl/dataframe.cpp @@ -852,8 +852,7 @@ const data_source &dataframe::get_data_source() const : *unsafe_get(source).other; } -std::string_view dataframe::get_series_info( - const std::string_view &id, +std::string_view dataframe::get_series_info(const std::string &id, const char *key) const & { switch (auto &&ser = get_data_source().get_series(id)) { diff --git a/src/dataframe/impl/dataframe.h b/src/dataframe/impl/dataframe.h index e61f46b6d..cfe7c7b66 100644 --- a/src/dataframe/impl/dataframe.h +++ b/src/dataframe/impl/dataframe.h @@ -122,9 +122,8 @@ class dataframe [[nodiscard]] series_meta_t get_series_meta( const std::string &id) const &; - [[nodiscard]] std::string_view get_series_info( - const std::string_view &id, - const char *key) const &; + [[nodiscard]] std::string_view + get_series_info(const std::string &id, const char *key) const &; [[nodiscard]] cell_reference get_data( const record_identifier &record_id, diff --git a/src/dataframe/interface.cpp b/src/dataframe/interface.cpp index a57b5389c..2f94b2896 100644 --- a/src/dataframe/interface.cpp +++ b/src/dataframe/interface.cpp @@ -183,7 +183,7 @@ series_meta_t dataframe_interface::get_series_meta( } std::string_view dataframe_interface::get_series_info( - const std::string_view &id, + const std::string &id, const char *key) const & { return as_impl(this).get_series_info(id, key); diff --git a/src/dataframe/interface.h b/src/dataframe/interface.h index 96af2fb24..02cc149ee 100644 --- a/src/dataframe/interface.h +++ b/src/dataframe/interface.h @@ -165,9 +165,8 @@ class alignas(align_impl) dataframe_interface [[nodiscard]] series_meta_t get_series_meta( const std::string &id) const &; - [[nodiscard]] std::string_view get_series_info( - const std::string_view &id, - const char *key) const &; + [[nodiscard]] std::string_view + get_series_info(const std::string &id, const char *key) const &; [[nodiscard]] bool is_removed(std::size_t record_id) const &; diff --git a/src/dataframe/old/types.h b/src/dataframe/old/types.h index a632d7ae7..8c66947db 100644 --- a/src/dataframe/old/types.h +++ b/src/dataframe/old/types.h @@ -117,6 +117,15 @@ class Filter }; } + Fun *getFun1() const + { + return func1.get() == True ? nullptr : func1.get(); + } + Fun *getFun2() const + { + return func2.get() == True ? nullptr : func2.get(); + } + private: SharedFun func1{std::shared_ptr{}, True}; SharedFun func2{std::shared_ptr{}, True}; @@ -167,7 +176,7 @@ struct MarkerId struct DataTable { [[nodiscard]] virtual std::string_view get_series_info( - const std::string_view &id, + const std::string &id, const char *key) const & = 0; [[nodiscard]] virtual dataframe::series_meta_t get_series_meta( @@ -215,7 +224,7 @@ struct DataTableImpl final : DataTable {} [[nodiscard]] std::string_view get_series_info( - const std::string_view &id, + const std::string &id, const char *key) const & override { return df->get_series_info(id, key); diff --git a/test/unit/chart/events.cpp b/test/unit/chart/events.cpp index 38fec2389..d7a256e67 100644 --- a/test/unit/chart/events.cpp +++ b/test/unit/chart/events.cpp @@ -271,7 +271,8 @@ struct chart_setup skip->*is_emscripten == "Emscripten build"_is_false; return is_emscripten; }()}; - Vizzu::Chart chart{}; + Vizzu::Chart chart{std::make_shared( + Vizzu::dataframe::dataframe::create_new())}; explicit(false) operator Vizzu::Chart &() { From a91eef8f215eb73df01fbf63e87d5bc7ae6a7a91 Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Thu, 13 Feb 2025 11:09:36 +0100 Subject: [PATCH 04/13] api v1 --- src/apps/weblib/cinterface.cpp | 1 + src/apps/weblib/interface.cpp | 6 +- src/apps/weblib/jswrappers.h | 20 ++++++- src/apps/weblib/objectregistry.h | 2 +- src/apps/weblib/ts-api/chart.ts | 31 +++++++--- src/apps/weblib/ts-api/data.ts | 38 +++++++++--- src/apps/weblib/ts-api/datarecord.ts | 2 +- src/apps/weblib/ts-api/module/cchart.ts | 44 +++++++++++++- src/apps/weblib/ts-api/module/cdata.ts | 45 +------------- src/apps/weblib/ts-api/module/module.ts | 74 ++++++++++++++++++++++++ src/apps/weblib/ts-api/vizzu.ts | 11 +++- src/apps/weblib/typeschema-api/data.yaml | 4 +- 12 files changed, 209 insertions(+), 69 deletions(-) diff --git a/src/apps/weblib/cinterface.cpp b/src/apps/weblib/cinterface.cpp index 345b21139..220c82f57 100644 --- a/src/apps/weblib/cinterface.cpp +++ b/src/apps/weblib/cinterface.cpp @@ -23,6 +23,7 @@ static_assert(offsetof(Point, y) == 8); static_assert(offsetof(Value, dimension) == 0); static_assert(offsetof(Value, measureValue) == 8); static_assert(offsetof(Value, dimensionValue) == 8); +static_assert(sizeof(const char *) == 4); constexpr std::uint_fast32_t hash(std::string_view s) noexcept { diff --git a/src/apps/weblib/interface.cpp b/src/apps/weblib/interface.cpp index f5a44fc98..4abcec809 100644 --- a/src/apps/weblib/interface.cpp +++ b/src/apps/weblib/interface.cpp @@ -435,7 +435,6 @@ struct IntFDataTable final : Data::DataTable { auto &intf = Interface::getInstance(); auto &&res = dataframe::dataframe::create_new(); - auto &&obj = intf.createData(&res); std::vector aggregateBy(aggregate_by.size()); std::ranges::transform(aggregate_by, @@ -479,7 +478,7 @@ struct IntFDataTable final : Data::DataTable std::vector aggregatingRawNames( aggregating.size()); - externalData.values.aggregator(obj, + externalData.values.aggregator(intf.createData(&res), filter.getFun1(), filter.getFun2(), aggregateBy.size(), @@ -494,8 +493,6 @@ struct IntFDataTable final : Data::DataTable externalData.values.stringDeleter); } - intf.freeObj(obj); - std::map resMap; for (auto it = aggregatingNames.data(); auto &&agg : aggregating) @@ -506,6 +503,7 @@ struct IntFDataTable final : Data::DataTable Refl::enum_name(agg.getAggr()) + " of " + agg.getColIndex(); + res->finalize(); return {res, resMap}; } }; diff --git a/src/apps/weblib/jswrappers.h b/src/apps/weblib/jswrappers.h index e0c6adbe2..558160574 100644 --- a/src/apps/weblib/jswrappers.h +++ b/src/apps/weblib/jswrappers.h @@ -17,18 +17,32 @@ template struct JsCompositionWrapper { T values; void (*deleter)(); - ~JsCompositionWrapper() { deleter(); } + ~JsCompositionWrapper() + { + if (auto &&d = std::exchange(deleter, {})) d(); + } [[nodiscard]] JsCompositionWrapper(T &&values, void (*deleter)()) : values(std::move(values)), deleter(deleter) {} JsCompositionWrapper(const JsCompositionWrapper &) = delete; - JsCompositionWrapper(JsCompositionWrapper &&) noexcept = default; + JsCompositionWrapper(JsCompositionWrapper &&other) noexcept : + values(std::move(other.values)), + deleter(std::exchange(other.deleter, {})) + {} JsCompositionWrapper &operator=( const JsCompositionWrapper &) = delete; JsCompositionWrapper &operator=( - JsCompositionWrapper &&) noexcept = default; + JsCompositionWrapper &&other) noexcept + { + if (this->deleter != other.deleter) { + if (deleter) deleter(); + values = std::move(other.values); + deleter = std::exchange(other.deleter, {}); + } + return *this; + } }; } diff --git a/src/apps/weblib/objectregistry.h b/src/apps/weblib/objectregistry.h index 35bc71bca..115c8cf4c 100644 --- a/src/apps/weblib/objectregistry.h +++ b/src/apps/weblib/objectregistry.h @@ -46,7 +46,7 @@ template class ObjectRegistry if (auto lock = std::lock_guard{mutex}; objects.erase(handle)) return; - throw std::logic_error("No such object exists"); + // throw std::logic_error("No such object exists"); } private: diff --git a/src/apps/weblib/ts-api/chart.ts b/src/apps/weblib/ts-api/chart.ts index 98b577665..45e9cbfa4 100644 --- a/src/apps/weblib/ts-api/chart.ts +++ b/src/apps/weblib/ts-api/chart.ts @@ -5,12 +5,12 @@ import * as D from './types/data.js' import { Module } from './module/module.js' import { Chart as ChartInterface } from './module/chart.js' import { CChart, Snapshot } from './module/cchart.js' -import { CAnimControl, CAnimation } from './module/canimctrl.js' import { CData } from './module/cdata.js' +import { CAnimControl, CAnimation } from './module/canimctrl.js' import { Data } from './data.js' import { Events, EventType, EventHandler, EventMap } from './events.js' import { Mirrored } from './tsutils.js' -import { VizzuOptions } from './vizzu.js' +import { VizzuOptions } from "./vizzu.js"; import { AnimControl } from './animcontrol.js' import { PluginRegistry, @@ -38,23 +38,39 @@ export class Chart implements ChartInterface { private _cChart: CChart private _cCanvas: CCanvas private _module: Module - private _cData: CData private _data: Data private _events: Events private _plugins: PluginRegistry private _changed = true + private _createData(): CData { + if (this._options.otherSource) { + const { series, aggregate } = this._options.otherSource + return this._module.createExternalData( + new Map(series.map(info => [info.name, info.type === 'dimension'] as const)), + new Map(series.map(info => [info.name, new Map(Object.entries(info))] as const)), + (data: CData, filt1: number, filt2: number, grouping: D.SeriesList, aggregating: D.SeriesList): string[] => { + new Data(data, undefined, false).set(aggregate(this._data.getFilterByPtr(filt1) || '', this._data.getFilterByPtr(filt2) || '', grouping, aggregating)) + return data.getMetaInfo().series.filter(series => series.type === + 'measure') + .map(series => series.name) + } + ) + } + return this._module.createData() + } + constructor(module: Module, options: VizzuOptions, plugins: PluginRegistry) { this._options = options this._plugins = plugins this._module = module - this._cData = this._module.createData() - this._cChart = this._module.createChart(this._cData) + const cData = this._createData() + this._cChart = this._module.createChart(cData) this._module.registerChart(this._cChart, this) this._cCanvas = this._module.createCanvas() - this._data = new Data(this._cData, this._cChart.getId()) + this._data = new Data(cData, this._cChart, true) this._events = new Events(this._cChart) this._plugins.init(this._events) @@ -79,6 +95,7 @@ export class Chart implements ChartInterface { this._module.unregisterChart(this._cChart) this._cCanvas.free() this._cChart.free() + this._data.detach() } start(): void { @@ -199,7 +216,7 @@ export class Chart implements ChartInterface { } get data(): Mirrored { - return this._cData.getMetaInfo() + return this._data.getMetaInfo() } get config(): Mirrored { diff --git a/src/apps/weblib/ts-api/data.ts b/src/apps/weblib/ts-api/data.ts index fdbe719a2..5888764ba 100644 --- a/src/apps/weblib/ts-api/data.ts +++ b/src/apps/weblib/ts-api/data.ts @@ -1,18 +1,37 @@ import * as D from './types/data.js' -import { CRecord, CData } from './module/cdata.js' +import { CData } from './module/cdata.js' +import { CRecord, CChart } from './module/cchart.js' import { DataRecord } from './datarecord.js' import { Mirrored } from './tsutils.js' -import { CPointer } from './cvizzu.types'; +import { CPointer } from "./cvizzu.types"; type DataTypes = D.DimensionValue | D.MeasureValue export class Data { private _cData: CData - private _cChart: CPointer + private _cChart: CChart | undefined + private readonly _needDetach: boolean + private _filters : Map - constructor(cData: CData, cChart: CPointer) { + constructor(cData: CData, cChart: CChart | undefined, needDetach: boolean) { this._cData = cData this._cChart = cChart + this._needDetach = needDetach + this._filters = new Map() + } + + detach(): void { + if (this._needDetach) { + this._cData.free() + } + } + + getMetaInfo(): Mirrored { + return this._cData.getMetaInfo() + } + + getFilterByPtr(ptr: CPointer): string | null { + return this._filters.get(ptr) ?? null } set(obj?: D.TableBySeries | D.TableByRecords): void { @@ -259,12 +278,17 @@ export class Data { this._cData.addMeasure(name, unit, numbers) } - private _setFilter(filter: D.FilterCallback | null): void { + private _setFilter(filter: D.FilterCallback | string | null): void { + if (this._cChart === undefined) { + throw new Error('chart is not attached') + } if (typeof filter === 'function') { const callback = (cRecord: CRecord): boolean => filter(new DataRecord(cRecord)) - this._cData.setFilter(callback, this._cChart) + this._cChart.setFilter(callback) } else if (filter === null) { - this._cData.setFilter(null, this._cChart) + this._cChart.setFilter(null) + } else if (typeof filter === 'string') { + this._filters.set(this._cChart.setFilter((): boolean => false), filter) } else { throw new Error('data filter is not a function or null') } diff --git a/src/apps/weblib/ts-api/datarecord.ts b/src/apps/weblib/ts-api/datarecord.ts index b9bf923ce..aeaf614cc 100644 --- a/src/apps/weblib/ts-api/datarecord.ts +++ b/src/apps/weblib/ts-api/datarecord.ts @@ -1,5 +1,5 @@ import * as Data from './types/data.js' -import { CRecord } from './module/cdata.js' +import { CRecord } from './module/cchart.js' export class DataRecord implements Data.Record { [seriesName: Data.SeriesName]: Data.Value diff --git a/src/apps/weblib/ts-api/module/cchart.ts b/src/apps/weblib/ts-api/module/cchart.ts index 927729ab1..b7addfa2d 100644 --- a/src/apps/weblib/ts-api/module/cchart.ts +++ b/src/apps/weblib/ts-api/module/cchart.ts @@ -1,4 +1,4 @@ -import { CString, CFunction, CEventPtr } from '../cvizzu.types' +import { CString, CFunction, CEventPtr, CPointer, CRecordPtr } from '../cvizzu.types'; import * as Anim from '../types/anim.js' import * as Config from '../types/config.js' @@ -23,6 +23,31 @@ class CConfig extends CProxy {} class CStyle extends CProxy {} class CAnimOptions extends CProxy {} +export class CRecord extends CObject { + constructor(env: CEnv, recordPtr: CRecordPtr) { + super(() => recordPtr, env) + } + + getValue(columnName: string): string | number { + const col = this._toCString(columnName) + let ptr + let value + + try { + ptr = this._call(this._wasm._record_getValue)(col) + + if (this._wasm.getValue(ptr, 'i1')) { + value = this._fromCString(this._wasm.getValue(ptr + 8, 'i8*')) + } else { + value = this._wasm.getValue(ptr + 8, 'double') + } + } finally { + this._wasm._free(col) + } + return value + } +} + export class CChart extends CManagedObject { config: CConfig style: CStyle @@ -161,4 +186,21 @@ export class CChart extends CManagedObject { this._wasm._anim_setValue ) } + + setFilter(callback: ((record: CRecord) => boolean) | null): CPointer { + const callbackPtrs: [CPointer, CPointer] = [0, 0] + if (callback !== null) { + const f = (recordPtr: CRecordPtr): boolean => callback(new CRecord(this, recordPtr)) + callbackPtrs[0] = this._wasm.addFunction(f, 'ii') + const deleter = (ptr: CPointer): void => { + if (ptr !== callbackPtrs[0]) console.warn('Wrong pointer passed to destructor') + this._wasm.removeFunction(callbackPtrs[0]) + this._wasm.removeFunction(callbackPtrs[1]) + } + callbackPtrs[1] = this._wasm.addFunction(deleter, 'vi') + } + this._call(this._wasm._chart_setFilter)(...callbackPtrs) + + return callbackPtrs[0] + } } diff --git a/src/apps/weblib/ts-api/module/cdata.ts b/src/apps/weblib/ts-api/module/cdata.ts index f0089ba9b..da303c7a3 100644 --- a/src/apps/weblib/ts-api/module/cdata.ts +++ b/src/apps/weblib/ts-api/module/cdata.ts @@ -1,34 +1,8 @@ -import { CPointer, CRecordPtr } from '../cvizzu.types' -import { CObject, CEnv } from './cenv.js' +import { CManagedObject } from "./cenv.js"; import { Mirrored } from '../tsutils' import * as Data from '../types/data.js' -export class CRecord extends CObject { - constructor(env: CEnv, recordPtr: CRecordPtr) { - super(() => recordPtr, env) - } - - getValue(columnName: string): string | number { - const col = this._toCString(columnName) - let ptr - let value - - try { - ptr = this._call(this._wasm._record_getValue)(col) - - if (this._wasm.getValue(ptr, 'i1')) { - value = this._fromCString(this._wasm.getValue(ptr + 8, 'i8*')) - } else { - value = this._wasm.getValue(ptr + 8, 'double') - } - } finally { - this._wasm._free(col) - } - return value - } -} - -export class CData extends CObject { +export class CData extends CManagedObject { getMetaInfo(): Mirrored { const cInfo = this._call(this._wasm._data_metaInfo)() const info = this._fromCString(cInfo) @@ -127,19 +101,4 @@ export class CData extends CObject { this._wasm._free(ptrArr) } } - - setFilter(callback: ((record: CRecord) => boolean) | null, chartId: CPointer): void { - const callbackPtrs: [CPointer, CPointer] = [0, 0] - if (callback !== null) { - const f = (recordPtr: CRecordPtr): boolean => callback(new CRecord(this, recordPtr)) - callbackPtrs[0] = this._wasm.addFunction(f, 'ii') - const deleter = (ptr: CPointer): void => { - if (ptr !== callbackPtrs[0]) console.warn('Wrong pointer passed to destructor') - this._wasm.removeFunction(callbackPtrs[0]) - this._wasm.removeFunction(callbackPtrs[1]) - } - callbackPtrs[1] = this._wasm.addFunction(deleter, 'vi') - } - this._callStatic(this._wasm._chart_setFilter).bind(this, chartId)(...callbackPtrs) - } } diff --git a/src/apps/weblib/ts-api/module/module.ts b/src/apps/weblib/ts-api/module/module.ts index d6ee64493..f51e104b1 100644 --- a/src/apps/weblib/ts-api/module/module.ts +++ b/src/apps/weblib/ts-api/module/module.ts @@ -1,4 +1,5 @@ import { CPointer, CVizzu } from '../cvizzu.types'; +import * as Data from '../types/data.js' import { ObjectRegistry } from './objregistry.js' import { CEnv } from './cenv.js' @@ -9,6 +10,7 @@ import { CAnimControl } from './canimctrl.js' import { CCoordSystem } from './ccoordsys.js' import { Canvas } from './canvas.js' import { Chart } from './chart.js' +import { AggregatorType } from "../types/data.js"; export class Module extends CEnv { constructor(wasm: CVizzu) { @@ -57,6 +59,78 @@ export class Module extends CEnv { return new CData(this._getStatic(this._wasm._vizzu_createData), this) } + createExternalData(isDimension: Map, toInfo: Map>, + aggregatorFunction: (data: CData, + filt1: CPointer, + filt2: CPointer, + grouping: Data.SeriesList, + aggregating: Data.SeriesList) => string[] + ): CData { + const callbackPtrs: [CPointer, CPointer, CPointer, CPointer, CPointer] = [0, 0, 0, 0, 0] + const stringDeleter = (ptr: CPointer): void => { + this._wasm._free(ptr) + } + callbackPtrs[0] = this._wasm.addFunction(stringDeleter, 'vi') + + const seriesMeta = (series: CPointer): boolean => { + const res = isDimension.get(this._fromCString(series)) + if (res === undefined) throw new Error('Unknown series') + return res + } + callbackPtrs[1] = this._wasm.addFunction(seriesMeta, 'ii') + + const seriesInfo = (series: CPointer, key: CPointer): CPointer => { + const seriesName = this._fromCString(series) + const keyName = this._fromCString(key) + const info = toInfo.get(seriesName) + if (!info) throw new Error('Unknown series') + return this._toCString(info.get(keyName) || '') + } + callbackPtrs[2] = this._wasm.addFunction(seriesInfo, 'iii') + + const aggregator = (dataPtr: CPointer, filt1: CPointer, filt2: CPointer, + grouping: number, groupingList: CPointer, + aggregating: number, aggregatingNames: CPointer, + aggregatingFunctions: CPointer, outputNames: CPointer): void => { + const data = new CData((): CPointer => dataPtr, this) + + const groupingArray: Data.SeriesList = new Array(grouping) + for (let i = 0; i < grouping; i++) { + groupingArray[i] = { + name: this._fromCString(this._wasm.getValue(groupingList + i * 4, 'i8*')) + } + } + const aggregatingArray: Data.SeriesList = new Array(aggregating) + for (let i = 0; i < aggregating; i++) { + aggregatingArray[i] = { + name: this._fromCString(this._wasm.getValue(aggregatingNames + i * 4, 'i8*')), + aggregator: this._fromCString(this._wasm.getValue(aggregatingFunctions + i * 4, 'i8*')) as AggregatorType + } + this._wasm.setValue(outputNames + i * 4, 0, 'i8*') + } + + const aggregated = aggregatorFunction(data, filt1, filt2, groupingArray, aggregatingArray) + for (let i = 0; i < aggregating; i++) { + const val = aggregated[i] + if (val) { + this._wasm.setValue(outputNames + i * 4, this._toCString(val), 'i8*') + } + } + data.free() + } + callbackPtrs[3] = this._wasm.addFunction(aggregator, 'viiiiiiiii') + + const deleter = (): void => { + for (const ptr of callbackPtrs) { + this._wasm.removeFunction(ptr) + } + } + callbackPtrs[4] = this._wasm.addFunction(deleter, 'v') + + const cPointer = this._callStatic(this._wasm._vizzu_createExternalData)(...callbackPtrs) + return new CData((): CPointer => cPointer, this) + } + createChart(data : CData): CChart { const cPointer = this._callStatic(this._wasm._vizzu_createChart)(data.getId()) return new CChart(this, (): CPointer => cPointer) diff --git a/src/apps/weblib/ts-api/vizzu.ts b/src/apps/weblib/ts-api/vizzu.ts index 720502539..ac88d94a4 100644 --- a/src/apps/weblib/ts-api/vizzu.ts +++ b/src/apps/weblib/ts-api/vizzu.ts @@ -48,7 +48,16 @@ export interface FeatureOptions { features?: Plugin[] } -export type VizzuOptions = FeatureOptions & LazyCanvasOptions +export interface OtherSource { + series: Data.SeriesMetaInfo[] + aggregate: (filter1: string, filter2: string, groupBy: Data.SeriesList, series: Data.SeriesList) => Data.Set +} + +export interface OtherSourceOptions { + otherSource?: OtherSource +} + +export type VizzuOptions = FeatureOptions & OtherSourceOptions & LazyCanvasOptions export type FeatureFunction = (feature: Feature | Plugin, enabled?: boolean) => PluginApi export interface Features extends Record, FeatureFunction { diff --git a/src/apps/weblib/typeschema-api/data.yaml b/src/apps/weblib/typeschema-api/data.yaml index 8035a008f..d9ab489a7 100644 --- a/src/apps/weblib/typeschema-api/data.yaml +++ b/src/apps/weblib/typeschema-api/data.yaml @@ -335,7 +335,9 @@ definitions: description: | A filter callback is called on each record of the dataset on chart generation. If the callback returns false, the record will not be shown on the chart. - $ref: FilterCallback + oneOf: + - $ref: FilterCallback + - type: string nullable: true TableBySeries: From 804fd26f62f258cd921c2ed502686763a009a3bf Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Thu, 13 Feb 2025 12:32:33 +0100 Subject: [PATCH 05/13] filter deleter --- src/apps/weblib/ts-api/chart.ts | 2 +- src/apps/weblib/ts-api/data.ts | 2 +- src/apps/weblib/ts-api/module/cchart.ts | 3 ++- src/apps/weblib/ts-api/vizzu.ts | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/apps/weblib/ts-api/chart.ts b/src/apps/weblib/ts-api/chart.ts index 45e9cbfa4..9f476ca67 100644 --- a/src/apps/weblib/ts-api/chart.ts +++ b/src/apps/weblib/ts-api/chart.ts @@ -50,7 +50,7 @@ export class Chart implements ChartInterface { new Map(series.map(info => [info.name, info.type === 'dimension'] as const)), new Map(series.map(info => [info.name, new Map(Object.entries(info))] as const)), (data: CData, filt1: number, filt2: number, grouping: D.SeriesList, aggregating: D.SeriesList): string[] => { - new Data(data, undefined, false).set(aggregate(this._data.getFilterByPtr(filt1) || '', this._data.getFilterByPtr(filt2) || '', grouping, aggregating)) + new Data(data, undefined, false).set(aggregate(this._data.getFilterByPtr(filt1), this._data.getFilterByPtr(filt2), grouping, aggregating)) return data.getMetaInfo().series.filter(series => series.type === 'measure') .map(series => series.name) diff --git a/src/apps/weblib/ts-api/data.ts b/src/apps/weblib/ts-api/data.ts index 5888764ba..c3090673e 100644 --- a/src/apps/weblib/ts-api/data.ts +++ b/src/apps/weblib/ts-api/data.ts @@ -288,7 +288,7 @@ export class Data { } else if (filter === null) { this._cChart.setFilter(null) } else if (typeof filter === 'string') { - this._filters.set(this._cChart.setFilter((): boolean => false), filter) + this._filters.set(this._cChart.setFilter((): boolean => false, this._filters.delete), filter) } else { throw new Error('data filter is not a function or null') } diff --git a/src/apps/weblib/ts-api/module/cchart.ts b/src/apps/weblib/ts-api/module/cchart.ts index b7addfa2d..86c20348b 100644 --- a/src/apps/weblib/ts-api/module/cchart.ts +++ b/src/apps/weblib/ts-api/module/cchart.ts @@ -187,13 +187,14 @@ export class CChart extends CManagedObject { ) } - setFilter(callback: ((record: CRecord) => boolean) | null): CPointer { + setFilter(callback: ((record: CRecord) => boolean) | null, out_deleter: ((ptr: CPointer) => void) | null = null): CPointer { const callbackPtrs: [CPointer, CPointer] = [0, 0] if (callback !== null) { const f = (recordPtr: CRecordPtr): boolean => callback(new CRecord(this, recordPtr)) callbackPtrs[0] = this._wasm.addFunction(f, 'ii') const deleter = (ptr: CPointer): void => { if (ptr !== callbackPtrs[0]) console.warn('Wrong pointer passed to destructor') + if (out_deleter) out_deleter(callbackPtrs[0]) this._wasm.removeFunction(callbackPtrs[0]) this._wasm.removeFunction(callbackPtrs[1]) } diff --git a/src/apps/weblib/ts-api/vizzu.ts b/src/apps/weblib/ts-api/vizzu.ts index ac88d94a4..33a7c3f72 100644 --- a/src/apps/weblib/ts-api/vizzu.ts +++ b/src/apps/weblib/ts-api/vizzu.ts @@ -50,7 +50,7 @@ export interface FeatureOptions { export interface OtherSource { series: Data.SeriesMetaInfo[] - aggregate: (filter1: string, filter2: string, groupBy: Data.SeriesList, series: Data.SeriesList) => Data.Set + aggregate: (filter1: string | null, filter2: string | null, groupBy: Data.SeriesList, series: Data.SeriesList) => Data.Set } export interface OtherSourceOptions { From fc5c4a14bd999010b78f9167c0ebd447cb57e5d2 Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Thu, 13 Feb 2025 14:32:20 +0100 Subject: [PATCH 06/13] add sort + move datatable free to c++ side --- src/apps/weblib/interface.cpp | 16 +++++++++------- src/apps/weblib/objectregistry.h | 2 +- src/apps/weblib/ts-api/chart.ts | 5 +++-- src/apps/weblib/ts-api/data.ts | 4 ++-- src/apps/weblib/ts-api/module/cenv.ts | 6 ++++-- src/apps/weblib/ts-api/module/module.ts | 3 +-- 6 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/apps/weblib/interface.cpp b/src/apps/weblib/interface.cpp index 4abcec809..0f616b7b4 100644 --- a/src/apps/weblib/interface.cpp +++ b/src/apps/weblib/interface.cpp @@ -478,7 +478,9 @@ struct IntFDataTable final : Data::DataTable std::vector aggregatingRawNames( aggregating.size()); - externalData.values.aggregator(intf.createData(&res), + externalData.values.aggregator( + create_unique_ptr(intf.createData(&res), &object_free) + .get(), filter.getFun1(), filter.getFun2(), aggregateBy.size(), @@ -496,12 +498,12 @@ struct IntFDataTable final : Data::DataTable std::map resMap; for (auto it = aggregatingNames.data(); auto &&agg : aggregating) - if (auto &&ptr = *it++) - resMap[agg] = ptr.get(); - else - resMap[agg] = - Refl::enum_name(agg.getAggr()) - + " of " + agg.getColIndex(); + resMap[agg] = it++->get(); + + for (const auto &dim : aggregate_by) + res->set_sort(dim.getColIndex(), + dataframe::sort_type::less, + dataframe::na_position::first); res->finalize(); return {res, resMap}; diff --git a/src/apps/weblib/objectregistry.h b/src/apps/weblib/objectregistry.h index 115c8cf4c..35bc71bca 100644 --- a/src/apps/weblib/objectregistry.h +++ b/src/apps/weblib/objectregistry.h @@ -46,7 +46,7 @@ template class ObjectRegistry if (auto lock = std::lock_guard{mutex}; objects.erase(handle)) return; - // throw std::logic_error("No such object exists"); + throw std::logic_error("No such object exists"); } private: diff --git a/src/apps/weblib/ts-api/chart.ts b/src/apps/weblib/ts-api/chart.ts index 9f476ca67..fcf1e71e9 100644 --- a/src/apps/weblib/ts-api/chart.ts +++ b/src/apps/weblib/ts-api/chart.ts @@ -50,7 +50,7 @@ export class Chart implements ChartInterface { new Map(series.map(info => [info.name, info.type === 'dimension'] as const)), new Map(series.map(info => [info.name, new Map(Object.entries(info))] as const)), (data: CData, filt1: number, filt2: number, grouping: D.SeriesList, aggregating: D.SeriesList): string[] => { - new Data(data, undefined, false).set(aggregate(this._data.getFilterByPtr(filt1), this._data.getFilterByPtr(filt2), grouping, aggregating)) + new Data(data).set(aggregate(this._data.getFilterByPtr(filt1), this._data.getFilterByPtr(filt2), grouping, aggregating)) return data.getMetaInfo().series.filter(series => series.type === 'measure') .map(series => series.name) @@ -65,12 +65,13 @@ export class Chart implements ChartInterface { this._plugins = plugins this._module = module + const cDataFromInside = true const cData = this._createData() this._cChart = this._module.createChart(cData) this._module.registerChart(this._cChart, this) this._cCanvas = this._module.createCanvas() - this._data = new Data(cData, this._cChart, true) + this._data = new Data(cData, this._cChart, cDataFromInside) this._events = new Events(this._cChart) this._plugins.init(this._events) diff --git a/src/apps/weblib/ts-api/data.ts b/src/apps/weblib/ts-api/data.ts index c3090673e..a7c2972c9 100644 --- a/src/apps/weblib/ts-api/data.ts +++ b/src/apps/weblib/ts-api/data.ts @@ -13,7 +13,7 @@ export class Data { private readonly _needDetach: boolean private _filters : Map - constructor(cData: CData, cChart: CChart | undefined, needDetach: boolean) { + constructor(cData: CData, cChart: CChart | undefined = undefined, needDetach: boolean = true) { this._cData = cData this._cChart = cChart this._needDetach = needDetach @@ -288,7 +288,7 @@ export class Data { } else if (filter === null) { this._cChart.setFilter(null) } else if (typeof filter === 'string') { - this._filters.set(this._cChart.setFilter((): boolean => false, this._filters.delete), filter) + this._filters.set(this._cChart.setFilter((): boolean => false, this._filters.delete.bind(this._filters)), filter) } else { throw new Error('data filter is not a function or null') } diff --git a/src/apps/weblib/ts-api/module/cenv.ts b/src/apps/weblib/ts-api/module/cenv.ts index 45de55855..77e8dda7b 100644 --- a/src/apps/weblib/ts-api/module/cenv.ts +++ b/src/apps/weblib/ts-api/module/cenv.ts @@ -68,9 +68,11 @@ export class CObject extends CEnv { } export class CManagedObject extends CObject { - constructor(getId: CPointerClosure, cenv: CEnv) { + constructor(getId: CPointerClosure, cenv: CEnv, manage: boolean = true) { super(getId, cenv) - this._objectRegistry.register(this.getId) + if (manage) { + this._objectRegistry.register(this.getId) + } } free(): void { diff --git a/src/apps/weblib/ts-api/module/module.ts b/src/apps/weblib/ts-api/module/module.ts index f51e104b1..6d880c35c 100644 --- a/src/apps/weblib/ts-api/module/module.ts +++ b/src/apps/weblib/ts-api/module/module.ts @@ -92,7 +92,7 @@ export class Module extends CEnv { grouping: number, groupingList: CPointer, aggregating: number, aggregatingNames: CPointer, aggregatingFunctions: CPointer, outputNames: CPointer): void => { - const data = new CData((): CPointer => dataPtr, this) + const data = new CData((): CPointer => dataPtr, this, false) const groupingArray: Data.SeriesList = new Array(grouping) for (let i = 0; i < grouping; i++) { @@ -116,7 +116,6 @@ export class Module extends CEnv { this._wasm.setValue(outputNames + i * 4, this._toCString(val), 'i8*') } } - data.free() } callbackPtrs[3] = this._wasm.addFunction(aggregator, 'viiiiiiiii') From fb5217315593c1c9798bd2834b40e6f1b5a824fe Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Thu, 13 Feb 2025 17:43:10 +0100 Subject: [PATCH 07/13] fix format --- src/apps/weblib/ts-api/chart.ts | 30 ++++++++++---- src/apps/weblib/ts-api/cvizzu.types.d.ts | 12 +++--- src/apps/weblib/ts-api/data.ts | 12 ++++-- src/apps/weblib/ts-api/module/cchart.ts | 7 +++- src/apps/weblib/ts-api/module/cdata.ts | 2 +- src/apps/weblib/ts-api/module/module.ts | 51 ++++++++++++++++-------- src/apps/weblib/ts-api/vizzu.ts | 7 +++- 7 files changed, 84 insertions(+), 37 deletions(-) diff --git a/src/apps/weblib/ts-api/chart.ts b/src/apps/weblib/ts-api/chart.ts index fcf1e71e9..08d0a52d3 100644 --- a/src/apps/weblib/ts-api/chart.ts +++ b/src/apps/weblib/ts-api/chart.ts @@ -10,7 +10,7 @@ import { CAnimControl, CAnimation } from './module/canimctrl.js' import { Data } from './data.js' import { Events, EventType, EventHandler, EventMap } from './events.js' import { Mirrored } from './tsutils.js' -import { VizzuOptions } from "./vizzu.js"; +import { VizzuOptions } from './vizzu.js' import { AnimControl } from './animcontrol.js' import { PluginRegistry, @@ -47,13 +47,27 @@ export class Chart implements ChartInterface { if (this._options.otherSource) { const { series, aggregate } = this._options.otherSource return this._module.createExternalData( - new Map(series.map(info => [info.name, info.type === 'dimension'] as const)), - new Map(series.map(info => [info.name, new Map(Object.entries(info))] as const)), - (data: CData, filt1: number, filt2: number, grouping: D.SeriesList, aggregating: D.SeriesList): string[] => { - new Data(data).set(aggregate(this._data.getFilterByPtr(filt1), this._data.getFilterByPtr(filt2), grouping, aggregating)) - return data.getMetaInfo().series.filter(series => series.type === - 'measure') - .map(series => series.name) + new Map(series.map((info) => [info.name, info.type === 'dimension'] as const)), + new Map(series.map((info) => [info.name, new Map(Object.entries(info))] as const)), + ( + data: CData, + filt1: number, + filt2: number, + grouping: D.SeriesList, + aggregating: D.SeriesList + ): string[] => { + new Data(data).set( + aggregate( + this._data.getFilterByPtr(filt1), + this._data.getFilterByPtr(filt2), + grouping, + aggregating + ) + ) + return data + .getMetaInfo() + .series.filter((series) => series.type === 'measure') + .map((series) => series.name) } ) } diff --git a/src/apps/weblib/ts-api/cvizzu.types.d.ts b/src/apps/weblib/ts-api/cvizzu.types.d.ts index 4b00f0fa6..50eac0aec 100644 --- a/src/apps/weblib/ts-api/cvizzu.types.d.ts +++ b/src/apps/weblib/ts-api/cvizzu.types.d.ts @@ -68,13 +68,13 @@ export interface CVizzu { // exported functions _vizzu_createData(): CDataPtr _vizzu_createExternalData( - stringDeleter : CFunction, - seriesMeta : CFunction, - seriesInfo : CFunction, - aggregator : CFunction, - deleter : CFunction + stringDeleter: CFunction, + seriesMeta: CFunction, + seriesInfo: CFunction, + aggregator: CFunction, + deleter: CFunction ): CDataPtr - _vizzu_createChart(data : CDataPtr): CChartPtr + _vizzu_createChart(data: CDataPtr): CChartPtr _vizzu_createCanvas(): CCanvasPtr _vizzu_pointerDown( chart: CChartPtr, diff --git a/src/apps/weblib/ts-api/data.ts b/src/apps/weblib/ts-api/data.ts index a7c2972c9..3281047bd 100644 --- a/src/apps/weblib/ts-api/data.ts +++ b/src/apps/weblib/ts-api/data.ts @@ -4,14 +4,14 @@ import { CData } from './module/cdata.js' import { CRecord, CChart } from './module/cchart.js' import { DataRecord } from './datarecord.js' import { Mirrored } from './tsutils.js' -import { CPointer } from "./cvizzu.types"; +import { CPointer } from './cvizzu.types' type DataTypes = D.DimensionValue | D.MeasureValue export class Data { private _cData: CData private _cChart: CChart | undefined private readonly _needDetach: boolean - private _filters : Map + private _filters: Map constructor(cData: CData, cChart: CChart | undefined = undefined, needDetach: boolean = true) { this._cData = cData @@ -288,7 +288,13 @@ export class Data { } else if (filter === null) { this._cChart.setFilter(null) } else if (typeof filter === 'string') { - this._filters.set(this._cChart.setFilter((): boolean => false, this._filters.delete.bind(this._filters)), filter) + this._filters.set( + this._cChart.setFilter( + (): boolean => false, + this._filters.delete.bind(this._filters) + ), + filter + ) } else { throw new Error('data filter is not a function or null') } diff --git a/src/apps/weblib/ts-api/module/cchart.ts b/src/apps/weblib/ts-api/module/cchart.ts index 86c20348b..4b2ddbcfe 100644 --- a/src/apps/weblib/ts-api/module/cchart.ts +++ b/src/apps/weblib/ts-api/module/cchart.ts @@ -1,4 +1,4 @@ -import { CString, CFunction, CEventPtr, CPointer, CRecordPtr } from '../cvizzu.types'; +import { CString, CFunction, CEventPtr, CPointer, CRecordPtr } from '../cvizzu.types' import * as Anim from '../types/anim.js' import * as Config from '../types/config.js' @@ -187,7 +187,10 @@ export class CChart extends CManagedObject { ) } - setFilter(callback: ((record: CRecord) => boolean) | null, out_deleter: ((ptr: CPointer) => void) | null = null): CPointer { + setFilter( + callback: ((record: CRecord) => boolean) | null, + out_deleter: ((ptr: CPointer) => void) | null = null + ): CPointer { const callbackPtrs: [CPointer, CPointer] = [0, 0] if (callback !== null) { const f = (recordPtr: CRecordPtr): boolean => callback(new CRecord(this, recordPtr)) diff --git a/src/apps/weblib/ts-api/module/cdata.ts b/src/apps/weblib/ts-api/module/cdata.ts index da303c7a3..1896e95f5 100644 --- a/src/apps/weblib/ts-api/module/cdata.ts +++ b/src/apps/weblib/ts-api/module/cdata.ts @@ -1,4 +1,4 @@ -import { CManagedObject } from "./cenv.js"; +import { CManagedObject } from './cenv.js' import { Mirrored } from '../tsutils' import * as Data from '../types/data.js' diff --git a/src/apps/weblib/ts-api/module/module.ts b/src/apps/weblib/ts-api/module/module.ts index 6d880c35c..24a985345 100644 --- a/src/apps/weblib/ts-api/module/module.ts +++ b/src/apps/weblib/ts-api/module/module.ts @@ -1,4 +1,4 @@ -import { CPointer, CVizzu } from '../cvizzu.types'; +import { CPointer, CVizzu } from '../cvizzu.types' import * as Data from '../types/data.js' import { ObjectRegistry } from './objregistry.js' @@ -10,7 +10,7 @@ import { CAnimControl } from './canimctrl.js' import { CCoordSystem } from './ccoordsys.js' import { Canvas } from './canvas.js' import { Chart } from './chart.js' -import { AggregatorType } from "../types/data.js"; +import { AggregatorType } from '../types/data.js' export class Module extends CEnv { constructor(wasm: CVizzu) { @@ -59,13 +59,17 @@ export class Module extends CEnv { return new CData(this._getStatic(this._wasm._vizzu_createData), this) } - createExternalData(isDimension: Map, toInfo: Map>, - aggregatorFunction: (data: CData, - filt1: CPointer, - filt2: CPointer, - grouping: Data.SeriesList, - aggregating: Data.SeriesList) => string[] - ): CData { + createExternalData( + isDimension: Map, + toInfo: Map>, + aggregatorFunction: ( + data: CData, + filt1: CPointer, + filt2: CPointer, + grouping: Data.SeriesList, + aggregating: Data.SeriesList + ) => string[] + ): CData { const callbackPtrs: [CPointer, CPointer, CPointer, CPointer, CPointer] = [0, 0, 0, 0, 0] const stringDeleter = (ptr: CPointer): void => { this._wasm._free(ptr) @@ -88,10 +92,17 @@ export class Module extends CEnv { } callbackPtrs[2] = this._wasm.addFunction(seriesInfo, 'iii') - const aggregator = (dataPtr: CPointer, filt1: CPointer, filt2: CPointer, - grouping: number, groupingList: CPointer, - aggregating: number, aggregatingNames: CPointer, - aggregatingFunctions: CPointer, outputNames: CPointer): void => { + const aggregator = ( + dataPtr: CPointer, + filt1: CPointer, + filt2: CPointer, + grouping: number, + groupingList: CPointer, + aggregating: number, + aggregatingNames: CPointer, + aggregatingFunctions: CPointer, + outputNames: CPointer + ): void => { const data = new CData((): CPointer => dataPtr, this, false) const groupingArray: Data.SeriesList = new Array(grouping) @@ -104,12 +115,20 @@ export class Module extends CEnv { for (let i = 0; i < aggregating; i++) { aggregatingArray[i] = { name: this._fromCString(this._wasm.getValue(aggregatingNames + i * 4, 'i8*')), - aggregator: this._fromCString(this._wasm.getValue(aggregatingFunctions + i * 4, 'i8*')) as AggregatorType + aggregator: this._fromCString( + this._wasm.getValue(aggregatingFunctions + i * 4, 'i8*') + ) as AggregatorType } this._wasm.setValue(outputNames + i * 4, 0, 'i8*') } - const aggregated = aggregatorFunction(data, filt1, filt2, groupingArray, aggregatingArray) + const aggregated = aggregatorFunction( + data, + filt1, + filt2, + groupingArray, + aggregatingArray + ) for (let i = 0; i < aggregating; i++) { const val = aggregated[i] if (val) { @@ -130,7 +149,7 @@ export class Module extends CEnv { return new CData((): CPointer => cPointer, this) } - createChart(data : CData): CChart { + createChart(data: CData): CChart { const cPointer = this._callStatic(this._wasm._vizzu_createChart)(data.getId()) return new CChart(this, (): CPointer => cPointer) } diff --git a/src/apps/weblib/ts-api/vizzu.ts b/src/apps/weblib/ts-api/vizzu.ts index 33a7c3f72..efe2b8848 100644 --- a/src/apps/weblib/ts-api/vizzu.ts +++ b/src/apps/weblib/ts-api/vizzu.ts @@ -50,7 +50,12 @@ export interface FeatureOptions { export interface OtherSource { series: Data.SeriesMetaInfo[] - aggregate: (filter1: string | null, filter2: string | null, groupBy: Data.SeriesList, series: Data.SeriesList) => Data.Set + aggregate: ( + filter1: string | null, + filter2: string | null, + groupBy: Data.SeriesList, + series: Data.SeriesList + ) => Data.Set } export interface OtherSourceOptions { From 8c708b04b2ae9dd33b34b02f4852d3b2dba0b769 Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Thu, 13 Feb 2025 18:35:05 +0100 Subject: [PATCH 08/13] clang-format --- src/chart/main/chart.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/chart/main/chart.cpp b/src/chart/main/chart.cpp index 789dee219..1a038fbd1 100644 --- a/src/chart/main/chart.cpp +++ b/src/chart/main/chart.cpp @@ -106,9 +106,9 @@ void Chart::draw(Gfx::ICanvas &canvas) { renderedChart = Draw::RenderedChart{ actPlot ? Draw::CoordinateSystem{layout.plotArea, - actPlot->getOptions()->angle, - actPlot->getOptions()->coordSystem, - actPlot->keepAspectRatio} + actPlot->getOptions()->angle, + actPlot->getOptions()->coordSystem, + actPlot->keepAspectRatio} : Draw::CoordinateSystem{layout.plotArea}, actPlot}; From dcffa226e0132a684c21fc6ce13cc7c8fe8efcad Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Tue, 18 Feb 2025 12:10:41 +0100 Subject: [PATCH 09/13] clang-tidy --- src/apps/weblib/interface.cpp | 6 ++++-- src/dataframe/old/types.h | 16 ++++++---------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/apps/weblib/interface.cpp b/src/apps/weblib/interface.cpp index 0f616b7b4..04d9a8b51 100644 --- a/src/apps/weblib/interface.cpp +++ b/src/apps/weblib/interface.cpp @@ -331,7 +331,8 @@ void Interface::addDimension(ObjectRegistryHandle table, getTable(table)->add_dimension({categories, categoriesCount}, {categoryIndices, categoryIndicesCount}, name, - {{{"isContiguous", isContiguous ? "true" : "false"}}}); + {{{"isContiguous", isContiguous ? "true" : "false"}}}, + dataframe::adding_type::create_or_override); } void Interface::addMeasure(ObjectRegistryHandle table, @@ -342,7 +343,8 @@ void Interface::addMeasure(ObjectRegistryHandle table, { getTable(table)->add_measure({values, count}, name, - {{std::pair{"unit", unit}}}); + {{std::pair{"unit", unit}}}, + dataframe::adding_type::create_or_override); } void Interface::addRecord(ObjectRegistryHandle table, diff --git a/src/dataframe/old/types.h b/src/dataframe/old/types.h index 8c66947db..0938f022a 100644 --- a/src/dataframe/old/types.h +++ b/src/dataframe/old/types.h @@ -117,11 +117,11 @@ class Filter }; } - Fun *getFun1() const + [[nodiscard]] Fun *getFun1() const { return func1.get() == True ? nullptr : func1.get(); } - Fun *getFun2() const + [[nodiscard]] Fun *getFun2() const { return func2.get() == True ? nullptr : func2.get(); } @@ -186,17 +186,13 @@ struct DataTable std::span dimension_categories, std::span dimension_values, std::string_view name, - std::span> info = - {}, - dataframe::adding_type adding_strategy = - dataframe::adding_type::create_or_override) & = 0; + std::span> info, + dataframe::adding_type adding_strategy) & = 0; virtual void add_measure(std::span measure_values, std::string_view name, - std::span> info = - {}, - dataframe::adding_type adding_strategy = - dataframe::adding_type::create_or_override) & = 0; + std::span> info, + dataframe::adding_type adding_strategy) & = 0; virtual void add_record( std::span values) & = 0; From 50d35675a97d9dce4eadff00dffe0ef4b9f68838 Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Tue, 18 Feb 2025 12:31:05 +0100 Subject: [PATCH 10/13] fix tests --- test/qtest/chart.cpp | 15 ++++++++++---- test/unit/chart/events.cpp | 40 +++++++++++++++++++++++++++----------- 2 files changed, 40 insertions(+), 15 deletions(-) diff --git a/test/qtest/chart.cpp b/test/qtest/chart.cpp index 151099f3f..3ba1933f9 100644 --- a/test/qtest/chart.cpp +++ b/test/qtest/chart.cpp @@ -9,7 +9,10 @@ #include "chart/ui/events.h" #include "dataframe/old/datatable.h" -TestChart::TestChart() {} +TestChart::TestChart() : + chart(std::make_shared( + Vizzu::dataframe::dataframe::create_new())) +{} void TestChart::prepareData() { @@ -28,11 +31,15 @@ void TestChart::prepareData() auto &table = chart.getChart().getTable(); table.add_dimension(cat1, std::array{0u, 0u, 0u, 1u, 1u, 1u, 2u, 2u, 2u}, - "Cat1"); + "Cat1", + {}, + {}); table.add_dimension(cat2, std::array{0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u}, - "Cat2"); - table.add_measure(val, "Val"); + "Cat2", + {}, + {}); + table.add_measure(val, "Val", {}, {}); chart.getChart() .getEventDispatcher() diff --git a/test/unit/chart/events.cpp b/test/unit/chart/events.cpp index d7a256e67..f66455971 100644 --- a/test/unit/chart/events.cpp +++ b/test/unit/chart/events.cpp @@ -60,28 +60,40 @@ auto testcase_0 = [](Vizzu::Data::DataTable &table) { table.add_dimension(std::initializer_list{}, std::initializer_list{}, - "Index"); - table.add_measure(std::initializer_list{}, "x"); - table.add_measure(std::initializer_list{}, "y"); + "Index", + {}, + {}); + table.add_measure(std::initializer_list{}, "x", {}, {}); + table.add_measure(std::initializer_list{}, "y", {}, {}); }; auto testcase_1 = [](Vizzu::Data::DataTable &table) { table.add_dimension({{"A", "B", "C", "D", "E"}}, {{0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4}}, - "Dim5"); + "Dim5", + {}, + {}); table.add_dimension({{"a", "b"}}, {{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1}}, - "Dim2"); + "Dim2", + {}, + {}); table.add_dimension({{"a", "b", "c"}}, {{0, 0, 1, 1, 0, 0, 1, 1, 2, 2, 1, 0, 2, 2, 1, 0}}, - "Dim3"); + "Dim3", + {}, + {}); table.add_measure( {{1, 2, 4, 3, 3, 4, 2, 1, 4, 3, 1, 2, 2, 1, 3, 4}}, - "Meas1"); + "Meas1", + {}, + {}); table.add_measure( {{0, -1, 5, 6, 6, 5, -1, 0, 5, 6, 0, -1, -1, 0, 6, -5}}, - "Meas2"); + "Meas2", + {}, + {}); }; auto testcase_2 = [](Vizzu::Data::DataTable &table) @@ -140,7 +152,9 @@ break)", 2, 2, 2}}, - "Channel title for long names"); + "Channel title for long names", + {}, + {}); table.add_dimension({{"Very long label of this element", "", @@ -196,7 +210,9 @@ break)", 9, }}, - "Childs of long names which have no end"); + "Childs of long names which have no end", + {}, + {}); table.add_measure({{639, 354, @@ -246,7 +262,9 @@ break)", 778, 651, 598}}, - "値3"); + "値3", + {}, + {}); }; struct chart_setup From aa1ff57544655fcd39403ed524019e084e268d0a Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Tue, 18 Feb 2025 15:58:40 +0100 Subject: [PATCH 11/13] other clang-tidy --- src/chart/main/chart.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/chart/main/chart.h b/src/chart/main/chart.h index 3359029f1..b901b5953 100644 --- a/src/chart/main/chart.h +++ b/src/chart/main/chart.h @@ -28,12 +28,12 @@ class Chart public: Util::Event<> onChanged; - Chart(const std::shared_ptr &table); + explicit Chart(const std::shared_ptr &table); Chart(Chart &&) noexcept = delete; void draw(Gfx::ICanvas &canvas); void setBoundRect(const Geom::Rect &rect); - Data::DataTable &getTable() const { return *table; } + [[nodiscard]] Data::DataTable &getTable() const { return *table; } Styles::Sheet &getStylesheet() { return stylesheet; } Styles::Chart &getStyles() { return actStyles; } [[nodiscard]] const Styles::Chart &getComputedStyles() const From f4e3d153b641420788c542ba0972efc3c0fba473 Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Tue, 18 Feb 2025 18:11:26 +0100 Subject: [PATCH 12/13] self-review --- src/apps/weblib/interface.cpp | 7 +++-- src/apps/weblib/ts-api/data.ts | 35 +++++++++++++----------- src/apps/weblib/ts-api/vizzu.ts | 4 +-- src/apps/weblib/typeschema-api/data.yaml | 9 +++++- tools/ci/type/gen-dts.cjs | 2 ++ 5 files changed, 35 insertions(+), 22 deletions(-) diff --git a/src/apps/weblib/interface.cpp b/src/apps/weblib/interface.cpp index 04d9a8b51..a05231bf6 100644 --- a/src/apps/weblib/interface.cpp +++ b/src/apps/weblib/interface.cpp @@ -480,9 +480,10 @@ struct IntFDataTable final : Data::DataTable std::vector aggregatingRawNames( aggregating.size()); - externalData.values.aggregator( - create_unique_ptr(intf.createData(&res), &object_free) - .get(), + auto &&dataPtr = create_unique_ptr(intf.createData(&res), + &object_free); + + externalData.values.aggregator(dataPtr.get(), filter.getFun1(), filter.getFun2(), aggregateBy.size(), diff --git a/src/apps/weblib/ts-api/data.ts b/src/apps/weblib/ts-api/data.ts index 3281047bd..d3566020c 100644 --- a/src/apps/weblib/ts-api/data.ts +++ b/src/apps/weblib/ts-api/data.ts @@ -8,20 +8,20 @@ import { CPointer } from './cvizzu.types' type DataTypes = D.DimensionValue | D.MeasureValue export class Data { - private _cData: CData - private _cChart: CChart | undefined - private readonly _needDetach: boolean - private _filters: Map + private readonly _cData: CData + private readonly _cChart: CChart | undefined + private readonly _dataOwned: boolean + private readonly _filters: Map - constructor(cData: CData, cChart: CChart | undefined = undefined, needDetach: boolean = true) { + constructor(cData: CData, cChart: CChart | undefined = undefined, dataOwned: boolean = true) { this._cData = cData this._cChart = cChart - this._needDetach = needDetach + this._dataOwned = dataOwned this._filters = new Map() } detach(): void { - if (this._needDetach) { + if (this._dataOwned) { this._cData.free() } } @@ -30,7 +30,7 @@ export class Data { return this._cData.getMetaInfo() } - getFilterByPtr(ptr: CPointer): string | null { + getFilterByPtr(ptr: CPointer): D.OutFilterCallback | null { return this._filters.get(ptr) ?? null } @@ -278,22 +278,25 @@ export class Data { this._cData.addMeasure(name, unit, numbers) } - private _setFilter(filter: D.FilterCallback | string | null): void { + private _setFilter(filter: D.FilterCallback | D.OutFilterCallback | null): void { if (this._cChart === undefined) { throw new Error('chart is not attached') } - if (typeof filter === 'function') { - const callback = (cRecord: CRecord): boolean => filter(new DataRecord(cRecord)) - this._cChart.setFilter(callback) - } else if (filter === null) { + if (filter === null) { this._cChart.setFilter(null) - } else if (typeof filter === 'string') { + } else if (typeof filter === 'function' && filter.length == 1) { + const callback = (cRecord: CRecord): boolean => + (filter as D.FilterCallback)(new DataRecord(cRecord)) + this._cChart.setFilter(callback) + } else if (typeof filter === 'function' && filter.length == 0) { this._filters.set( this._cChart.setFilter( (): boolean => false, - this._filters.delete.bind(this._filters) + (ptr: CPointer) => { + this._filters.delete(ptr) + } ), - filter + filter as D.OutFilterCallback ) } else { throw new Error('data filter is not a function or null') diff --git a/src/apps/weblib/ts-api/vizzu.ts b/src/apps/weblib/ts-api/vizzu.ts index efe2b8848..247093ce7 100644 --- a/src/apps/weblib/ts-api/vizzu.ts +++ b/src/apps/weblib/ts-api/vizzu.ts @@ -51,8 +51,8 @@ export interface FeatureOptions { export interface OtherSource { series: Data.SeriesMetaInfo[] aggregate: ( - filter1: string | null, - filter2: string | null, + filter1: Data.OutFilterCallback | null, + filter2: Data.OutFilterCallback | null, groupBy: Data.SeriesList, series: Data.SeriesList ) => Data.Set diff --git a/src/apps/weblib/typeschema-api/data.yaml b/src/apps/weblib/typeschema-api/data.yaml index d9ab489a7..3b8419423 100644 --- a/src/apps/weblib/typeschema-api/data.yaml +++ b/src/apps/weblib/typeschema-api/data.yaml @@ -315,6 +315,13 @@ definitions: required: [record] return: { type: boolean } + OutFilterCallback: + type: function + description: A function returns a specific value for source data + arguments: + required: [] + return: { type: any } + SeriesMetaInfo: $ref: SeriesInfo @@ -337,7 +344,7 @@ definitions: generation. If the callback returns false, the record will not be shown on the chart. oneOf: - $ref: FilterCallback - - type: string + - $ref: OutFilterCallback nullable: true TableBySeries: diff --git a/tools/ci/type/gen-dts.cjs b/tools/ci/type/gen-dts.cjs index 54ecdec66..db50e4cc9 100644 --- a/tools/ci/type/gen-dts.cjs +++ b/tools/ci/type/gen-dts.cjs @@ -222,6 +222,8 @@ class DTSGenerator { } const returns = definition.return ? this._getType(name, definition.return) : 'void' return `(${args}) => ${returns}` + } else if (definition.type === 'any') { + return 'unknown' } else if (definition.$ref) { this._validateDef(definition, '$ref', '$template') const refType = this._getRef(definition.$ref) From ba7d1412749cac3afbde3369dd4f6d80d1285eb6 Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Fri, 21 Feb 2025 10:50:38 +0100 Subject: [PATCH 13/13] default no filter --- src/apps/weblib/ts-api/data.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apps/weblib/ts-api/data.ts b/src/apps/weblib/ts-api/data.ts index d3566020c..de1b9ba49 100644 --- a/src/apps/weblib/ts-api/data.ts +++ b/src/apps/weblib/ts-api/data.ts @@ -291,7 +291,7 @@ export class Data { } else if (typeof filter === 'function' && filter.length == 0) { this._filters.set( this._cChart.setFilter( - (): boolean => false, + (): boolean => true, (ptr: CPointer) => { this._filters.delete(ptr) }