From 299409a22aed85bcac60664858e30dd4410f50df Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Fri, 17 May 2024 17:15:49 +0200 Subject: [PATCH 1/4] - Plot separation: No table or cube dependency. - Merge axises - Delete channelStats, move info to axises --- src/chart/animator/animation.cpp | 14 +- src/chart/animator/animation.h | 8 +- src/chart/animator/animator.cpp | 13 +- src/chart/animator/animator.h | 4 +- src/chart/animator/keyframe.cpp | 19 +- src/chart/animator/keyframe.h | 4 +- src/chart/animator/morph.cpp | 98 ++--- src/chart/animator/planner.cpp | 95 ++--- src/chart/generator/axis.cpp | 12 +- src/chart/generator/axis.h | 36 +- src/chart/generator/channelstats.cpp | 36 -- src/chart/generator/channelstats.h | 52 --- src/chart/generator/guides.cpp | 2 +- src/chart/generator/guides.h | 2 +- src/chart/generator/marker.cpp | 12 +- src/chart/generator/marker.h | 6 +- src/chart/generator/plot.cpp | 450 +--------------------- src/chart/generator/plot.h | 46 +-- src/chart/generator/plotbuilder.cpp | 482 ++++++++++++++++++++++++ src/chart/generator/plotbuilder.h | 48 +++ src/chart/main/chart.cpp | 11 +- src/chart/main/stylesheet.cpp | 2 +- src/chart/options/channels.h | 2 + src/chart/rendering/drawaxes.cpp | 12 +- src/chart/rendering/drawguides.cpp | 4 +- src/chart/rendering/drawinterlacing.cpp | 6 +- src/chart/rendering/drawlegend.cpp | 6 +- src/chart/rendering/markerrenderer.cpp | 4 +- 28 files changed, 740 insertions(+), 746 deletions(-) delete mode 100644 src/chart/generator/channelstats.cpp delete mode 100644 src/chart/generator/channelstats.h create mode 100644 src/chart/generator/plotbuilder.cpp create mode 100644 src/chart/generator/plotbuilder.h diff --git a/src/chart/animator/animation.cpp b/src/chart/animator/animation.cpp index cd8aa4dcd..696b1bbb0 100644 --- a/src/chart/animator/animation.cpp +++ b/src/chart/animator/animation.cpp @@ -4,12 +4,15 @@ #include "chart/animator/keyframe.h" #include "chart/generator/plot.h" +#include "chart/generator/plotbuilder.h" namespace Vizzu::Anim { -Animation::Animation(const Gen::PlotPtr &plot) : +Animation::Animation(const Data::DataTable &dataTable, + const Gen::PlotPtr &plot) : ::Anim::Control(static_cast(*this)), + dataTable(dataTable), source(plot), target(plot) { @@ -162,7 +165,7 @@ void Animation::addKeyframe(const Gen::PlotPtr &next, template Gen::PlotPtr Animation::getIntermediate(const Gen::PlotPtr &base, const Gen::PlotPtr &other, - Modifier &&modifier) + Modifier &&modifier) const { Gen::PlotPtr res; @@ -173,9 +176,9 @@ Gen::PlotPtr Animation::getIntermediate(const Gen::PlotPtr &base, if (*extOptions != *other->getOptions() && *extOptions != *base->getOptions()) { - res = std::make_shared(base->getTable(), - extOptions, - base->getStyle()); + res = + Gen::PlotBuilder{dataTable, extOptions, base->getStyle()} + .build(); res->keepAspectRatio = base->keepAspectRatio; } @@ -189,6 +192,7 @@ void Animation::addKeyframe(const Gen::PlotPtr &source, { ::Anim::Sequence::addKeyframe(std::make_shared(source, target, + dataTable, &options, isInstant)); } diff --git a/src/chart/animator/animation.h b/src/chart/animator/animation.h index 92026dd47..81b06d843 100644 --- a/src/chart/animator/animation.h +++ b/src/chart/animator/animation.h @@ -18,7 +18,8 @@ class Animation : public ::Anim::Sequence, public ::Anim::Control Util::Event onPlotChanged; - explicit Animation(const Gen::PlotPtr &plot); + Animation(const Data::DataTable &dataTable, + const Gen::PlotPtr &plot); void addKeyframe(const Gen::PlotPtr &next, const Options::Keyframe &options); @@ -27,14 +28,15 @@ class Animation : public ::Anim::Sequence, public ::Anim::Control OnComplete &&onThisCompletes); private: + const Data::DataTable &dataTable; OnComplete completionCallback; Gen::PlotPtr source; Gen::PlotPtr target; template - static Gen::PlotPtr getIntermediate(const Gen::PlotPtr &base, + Gen::PlotPtr getIntermediate(const Gen::PlotPtr &base, const Gen::PlotPtr &other, - Modifier &&modifier); + Modifier &&modifier) const; void addKeyframe(const Gen::PlotPtr &source, const Gen::PlotPtr &target, diff --git a/src/chart/animator/animator.cpp b/src/chart/animator/animator.cpp index d3ba305b0..d771c1e00 100644 --- a/src/chart/animator/animator.cpp +++ b/src/chart/animator/animator.cpp @@ -5,12 +5,16 @@ namespace Vizzu::Anim { -Animator::Animator(const Util::EventDispatcher::Event &onBegin, +Animator::Animator(const Data::DataTable &dataTable, + const Util::EventDispatcher::Event &onBegin, const Util::EventDispatcher::Event &onComplete) : + dataTable(dataTable), onBegin(onBegin), onComplete(onComplete), - actAnimation(std::make_shared(Gen::PlotPtr())), - nextAnimation(std::make_shared(Gen::PlotPtr())) + actAnimation( + std::make_shared(dataTable, Gen::PlotPtr())), + nextAnimation( + std::make_shared(dataTable, Gen::PlotPtr())) {} void Animator::addKeyframe(const Gen::PlotPtr &plot, @@ -36,7 +40,8 @@ void Animator::animate(const ::Anim::Control::Option &options, onThisCompletes.attach( [this](const Gen::PlotPtr &plot, const bool &) { - nextAnimation = std::make_shared(plot); + nextAnimation = + std::make_shared(dataTable, plot); this->running = false; }); diff --git a/src/chart/animator/animator.h b/src/chart/animator/animator.h index 8f466deac..880b732d6 100644 --- a/src/chart/animator/animator.h +++ b/src/chart/animator/animator.h @@ -15,7 +15,8 @@ namespace Vizzu::Anim class Animator { public: - Animator(const Util::EventDispatcher::Event &onBegin, + Animator(const Data::DataTable &dataTable, + const Util::EventDispatcher::Event &onBegin, const Util::EventDispatcher::Event &onComplete); void addKeyframe(const Gen::PlotPtr &plot, @@ -26,6 +27,7 @@ class Animator void animate(const ::Anim::Control::Option &options, Animation::OnComplete &&onThisCompletes); + const Data::DataTable &dataTable; Util::Event onDraw; Util::Event<> onProgress; std::reference_wrapper diff --git a/src/chart/animator/keyframe.cpp b/src/chart/animator/keyframe.cpp index acd7c0cb1..ce1f4b252 100644 --- a/src/chart/animator/keyframe.cpp +++ b/src/chart/animator/keyframe.cpp @@ -1,5 +1,6 @@ #include "keyframe.h" +#include #include #include "chart/generator/plot.h" @@ -9,18 +10,20 @@ namespace Vizzu::Anim Keyframe::Keyframe(Gen::PlotPtr src, const Gen::PlotPtr &trg, + const Data::DataTable &dataTable, const Options::Keyframe *options, bool isInstant) : options(*options), source(std::move(src)) { if (isInstant) this->options.all.duration = ::Anim::Duration(0); - init(trg); + init(trg, dataTable); prepareActual(); createPlan(*source, *target, *actual, this->options); } -void Keyframe::init(const Gen::PlotPtr &plot) +void Keyframe::init(const Gen::PlotPtr &plot, + const Data::DataTable &dataTable) { if (!plot) return; @@ -37,9 +40,9 @@ void Keyframe::init(const Gen::PlotPtr &plot) if (auto &&caption = source->getOptions()->caption.get()) emptyOpt->caption = caption; } - source = std::make_shared(plot->getTable(), - emptyOpt, - plot->getStyle()); + source = + Gen::PlotBuilder{dataTable, emptyOpt, plot->getStyle()} + .build(); source->keepAspectRatio = plot->keepAspectRatio; } target = plot; @@ -64,10 +67,8 @@ void Keyframe::prepareActual() prepareActualMarkersInfo(); } - auto options = - std::make_shared(*source->getOptions()); - - actual = std::make_shared(options, *source); + actual = std::make_shared(*source); + actual->detachOptions(); } void Keyframe::prepareActualMarkersInfo() diff --git a/src/chart/animator/keyframe.h b/src/chart/animator/keyframe.h index 576d1cc99..248747a85 100644 --- a/src/chart/animator/keyframe.h +++ b/src/chart/animator/keyframe.h @@ -14,6 +14,7 @@ class Keyframe : public Planner public: Keyframe(Gen::PlotPtr src, const Gen::PlotPtr &trg, + const Data::DataTable &dataTable, const Options::Keyframe *options, bool isInstant); @@ -29,7 +30,8 @@ class Keyframe : public Planner Gen::PlotPtr actual; Gen::PlotPtr targetCopy; - void init(const Gen::PlotPtr &plot); + void init(const Gen::PlotPtr &plot, + const Data::DataTable &dataTable); void prepareActual(); void prepareActualMarkersInfo(); void addMissingMarkers(const Gen::PlotPtr &source, diff --git a/src/chart/animator/morph.cpp b/src/chart/animator/morph.cpp index 2bbe1511c..b275078ea 100644 --- a/src/chart/animator/morph.cpp +++ b/src/chart/animator/morph.cpp @@ -106,19 +106,19 @@ void Horizontal::transform(const Gen::Plot &source, Gen::Plot &actual, double factor) const { - actual.commonAxises.at(Gen::ChannelId::x) = - interpolate(source.commonAxises.at(Gen::ChannelId::x), - target.commonAxises.at(Gen::ChannelId::x), + actual.axises.at(Gen::ChannelId::x).common = + interpolate(source.axises.at(Gen::ChannelId::x).common, + target.axises.at(Gen::ChannelId::x).common, factor); - actual.measureAxises.at(Gen::ChannelId::x) = - interpolate(source.measureAxises.at(Gen::ChannelId::x), - target.measureAxises.at(Gen::ChannelId::x), + actual.axises.at(Gen::ChannelId::x).measure = + interpolate(source.axises.at(Gen::ChannelId::x).measure, + target.axises.at(Gen::ChannelId::x).measure, factor); - actual.dimensionAxises.at(Gen::ChannelId::x) = - interpolate(source.dimensionAxises.at(Gen::ChannelId::x), - target.dimensionAxises.at(Gen::ChannelId::x), + actual.axises.at(Gen::ChannelId::x).dimension = + interpolate(source.axises.at(Gen::ChannelId::x).dimension, + target.axises.at(Gen::ChannelId::x).dimension, factor); actual.keepAspectRatio = interpolate(source.keepAspectRatio, @@ -184,42 +184,42 @@ void Vertical::transform(const Gen::Plot &source, Gen::Plot &actual, double factor) const { - actual.commonAxises.at(Gen::ChannelId::y) = - interpolate(source.commonAxises.at(Gen::ChannelId::y), - target.commonAxises.at(Gen::ChannelId::y), + actual.axises.at(Gen::ChannelId::y).common = + interpolate(source.axises.at(Gen::ChannelId::y).common, + target.axises.at(Gen::ChannelId::y).common, factor); - actual.measureAxises.at(Gen::ChannelId::y) = - interpolate(source.measureAxises.at(Gen::ChannelId::y), - target.measureAxises.at(Gen::ChannelId::y), + actual.axises.at(Gen::ChannelId::y).measure = + interpolate(source.axises.at(Gen::ChannelId::y).measure, + target.axises.at(Gen::ChannelId::y).measure, factor); - actual.dimensionAxises.at(Gen::ChannelId::y) = - interpolate(source.dimensionAxises.at(Gen::ChannelId::y), - target.dimensionAxises.at(Gen::ChannelId::y), + actual.axises.at(Gen::ChannelId::y).dimension = + interpolate(source.axises.at(Gen::ChannelId::y).dimension, + target.axises.at(Gen::ChannelId::y).dimension, factor); - actual.commonAxises.at(Gen::ChannelId::size) = - interpolate(source.commonAxises.at(Gen::ChannelId::size), - target.commonAxises.at(Gen::ChannelId::size), + actual.axises.at(Gen::ChannelId::size).common = + interpolate(source.axises.at(Gen::ChannelId::size).common, + target.axises.at(Gen::ChannelId::size).common, factor); - actual.measureAxises.at(Gen::ChannelId::size) = - interpolate(source.measureAxises.at(Gen::ChannelId::size), - target.measureAxises.at(Gen::ChannelId::size), + actual.axises.at(Gen::ChannelId::size).measure = + interpolate(source.axises.at(Gen::ChannelId::size).measure, + target.axises.at(Gen::ChannelId::size).measure, factor); - actual.dimensionAxises.at(Gen::ChannelId::size) = - interpolate(source.dimensionAxises.at(Gen::ChannelId::size), - target.dimensionAxises.at(Gen::ChannelId::size), + actual.axises.at(Gen::ChannelId::size).dimension = + interpolate(source.axises.at(Gen::ChannelId::size).dimension, + target.axises.at(Gen::ChannelId::size).dimension, factor); actual.guides.y = interpolate(source.guides.y, target.guides.y, factor); - actual.measureAxises.at(Gen::ChannelId::label) = - interpolate(source.measureAxises.at(Gen::ChannelId::label), - target.measureAxises.at(Gen::ChannelId::label), + actual.axises.at(Gen::ChannelId::label).measure = + interpolate(source.axises.at(Gen::ChannelId::label).measure, + target.axises.at(Gen::ChannelId::label).measure, factor); } @@ -243,35 +243,35 @@ void Morph::Color::transform(const Gen::Plot &source, Gen::Plot &actual, double factor) const { - actual.commonAxises.at(Gen::ChannelId::color) = - interpolate(source.commonAxises.at(Gen::ChannelId::color), - target.commonAxises.at(Gen::ChannelId::color), + actual.axises.at(Gen::ChannelId::color).common = + interpolate(source.axises.at(Gen::ChannelId::color).common, + target.axises.at(Gen::ChannelId::color).common, factor); - actual.measureAxises.at(Gen::ChannelId::color) = - interpolate(source.measureAxises.at(Gen::ChannelId::color), - target.measureAxises.at(Gen::ChannelId::color), + actual.axises.at(Gen::ChannelId::color).measure = + interpolate(source.axises.at(Gen::ChannelId::color).measure, + target.axises.at(Gen::ChannelId::color).measure, factor); - actual.dimensionAxises.at(Gen::ChannelId::color) = - interpolate(source.dimensionAxises.at(Gen::ChannelId::color), - target.dimensionAxises.at(Gen::ChannelId::color), + actual.axises.at(Gen::ChannelId::color).dimension = + interpolate(source.axises.at(Gen::ChannelId::color).dimension, + target.axises.at(Gen::ChannelId::color).dimension, factor); - actual.commonAxises.at(Gen::ChannelId::lightness) = - interpolate(source.commonAxises.at(Gen::ChannelId::lightness), - target.commonAxises.at(Gen::ChannelId::lightness), - factor); + actual.axises.at(Gen::ChannelId::lightness).common = interpolate( + source.axises.at(Gen::ChannelId::lightness).common, + target.axises.at(Gen::ChannelId::lightness).common, + factor); - actual.measureAxises.at(Gen::ChannelId::lightness) = interpolate( - source.measureAxises.at(Gen::ChannelId::lightness), - target.measureAxises.at(Gen::ChannelId::lightness), + actual.axises.at(Gen::ChannelId::lightness).measure = interpolate( + source.axises.at(Gen::ChannelId::lightness).measure, + target.axises.at(Gen::ChannelId::lightness).measure, factor); - actual.dimensionAxises.at(Gen::ChannelId::lightness) = + actual.axises.at(Gen::ChannelId::lightness).dimension = interpolate( - source.dimensionAxises.at(Gen::ChannelId::lightness), - target.dimensionAxises.at(Gen::ChannelId::lightness), + source.axises.at(Gen::ChannelId::lightness).dimension, + target.axises.at(Gen::ChannelId::lightness).dimension, factor); } diff --git a/src/chart/animator/planner.cpp b/src/chart/animator/planner.cpp index 798abd224..beb36471e 100644 --- a/src/chart/animator/planner.cpp +++ b/src/chart/animator/planner.cpp @@ -329,27 +329,28 @@ bool Planner::positionMorphNeeded() const bool Planner::needColor() const { return (isAnyLegend(Gen::ChannelId::color) - && (source->commonAxises.at(Gen::ChannelId::color) - != target->commonAxises.at( - Gen::ChannelId::color) - || source->dimensionAxises.at( - Gen::ChannelId::color) - != target->dimensionAxises.at( - Gen::ChannelId::color) - || source->measureAxises.at(Gen::ChannelId::color) - != target->measureAxises.at( - Gen::ChannelId::color))) + && (source->axises.at(Gen::ChannelId::color).common + != target->axises.at(Gen::ChannelId::color) + .common + || source->axises.at(Gen::ChannelId::color) + .dimension + != target->axises.at(Gen::ChannelId::color) + .dimension + || source->axises.at(Gen::ChannelId::color).measure + != target->axises.at(Gen::ChannelId::color) + .measure)) || (isAnyLegend(Gen::ChannelId::lightness) - && (source->commonAxises.at(Gen::ChannelId::lightness) - != target->commonAxises.at( - Gen::ChannelId::lightness) - || source->dimensionAxises.at( - Gen::ChannelId::lightness) - != target->dimensionAxises.at( - Gen::ChannelId::lightness) - || source->measureAxises.at(Gen::ChannelId::lightness) - != target->measureAxises.at( - Gen::ChannelId::lightness))) + && (source->axises.at(Gen::ChannelId::lightness).common + != target->axises.at(Gen::ChannelId::lightness) + .common + || source->axises.at(Gen::ChannelId::lightness) + .dimension + != target->axises.at(Gen::ChannelId::lightness) + .dimension + || source->axises.at(Gen::ChannelId::lightness) + .measure + != target->axises.at(Gen::ChannelId::lightness) + .measure)) || anyMarker(+[](const Gen::Marker &source, const Gen::Marker &target) -> bool { @@ -395,23 +396,23 @@ bool Planner::verticalBeforeHorizontal() const bool Planner::needVertical() const { - return source->commonAxises.at(Gen::ChannelId::y) - != target->commonAxises.at(Gen::ChannelId::y) - || source->measureAxises.at(Gen::ChannelId::y) - != target->measureAxises.at(Gen::ChannelId::y) - || source->dimensionAxises.at(Gen::ChannelId::y) - != target->dimensionAxises.at(Gen::ChannelId::y) + return source->axises.at(Gen::ChannelId::y).common + != target->axises.at(Gen::ChannelId::y).common + || source->axises.at(Gen::ChannelId::y).measure + != target->axises.at(Gen::ChannelId::y).measure + || source->axises.at(Gen::ChannelId::y).dimension + != target->axises.at(Gen::ChannelId::y).dimension || source->guides.at(Gen::ChannelId::y) != target->guides.at(Gen::ChannelId::y) || (isAnyLegend(Gen::ChannelId::size) - && (source->commonAxises.at(Gen::ChannelId::size) - != target->commonAxises.at(Gen::ChannelId::size) - || source->measureAxises.at(Gen::ChannelId::size) - != target->measureAxises.at( - Gen::ChannelId::size) - || source->dimensionAxises.at(Gen::ChannelId::size) - != target->dimensionAxises.at( - Gen::ChannelId::size))) + && (source->axises.at(Gen::ChannelId::size).common + != target->axises.at(Gen::ChannelId::size).common + || source->axises.at(Gen::ChannelId::size).measure + != target->axises.at(Gen::ChannelId::size) + .measure + || source->axises.at(Gen::ChannelId::size).dimension + != target->axises.at(Gen::ChannelId::size) + .dimension)) || source->anyAxisSet != target->anyAxisSet || (source->markerConnectionOrientation != target->markerConnectionOrientation @@ -421,13 +422,13 @@ bool Planner::needVertical() const || target->markerConnectionOrientation.value_or( Gen::Orientation::horizontal) == Gen::Orientation::vertical)) - || source->measureAxises.at(Gen::ChannelId::label) - .origMeasureName.get() - != target->measureAxises.at(Gen::ChannelId::label) - .origMeasureName.get() - || source->measureAxises.at(Gen::ChannelId::label).unit.get() - != target->measureAxises.at(Gen::ChannelId::label) - .unit.get() + || source->axises.at(Gen::ChannelId::label) + .measure.origMeasureName.get() + != target->axises.at(Gen::ChannelId::label) + .measure.origMeasureName.get() + || source->axises.at(Gen::ChannelId::label).measure.unit.get() + != target->axises.at(Gen::ChannelId::label) + .measure.unit.get() || anyMarker(+[](const Gen::Marker &source, const Gen::Marker &target) -> bool { @@ -442,12 +443,12 @@ bool Planner::needVertical() const bool Planner::needHorizontal() const { - return source->commonAxises.at(Gen::ChannelId::x) - != target->commonAxises.at(Gen::ChannelId::x) - || source->measureAxises.at(Gen::ChannelId::x) - != target->measureAxises.at(Gen::ChannelId::x) - || source->dimensionAxises.at(Gen::ChannelId::x) - != target->dimensionAxises.at(Gen::ChannelId::x) + return source->axises.at(Gen::ChannelId::x).common + != target->axises.at(Gen::ChannelId::x).common + || source->axises.at(Gen::ChannelId::x).measure + != target->axises.at(Gen::ChannelId::x).measure + || source->axises.at(Gen::ChannelId::x).dimension + != target->axises.at(Gen::ChannelId::x).dimension || source->guides.at(Gen::ChannelId::x) != target->guides.at(Gen::ChannelId::x) || source->anyAxisSet != target->anyAxisSet diff --git a/src/chart/generator/axis.cpp b/src/chart/generator/axis.cpp index 687c84132..2b72260a7 100644 --- a/src/chart/generator/axis.cpp +++ b/src/chart/generator/axis.cpp @@ -13,9 +13,10 @@ CommonAxis interpolate(const CommonAxis &op0, return {interpolate(op0.title, op1.title, factor)}; } -Geom::Point MeasureAxises::origo() const +Geom::Point Axises::origo() const { - return {at(ChannelId::x).origo(), at(ChannelId::y).origo()}; + return {at(ChannelId::x).measure.origo(), + at(ChannelId::y).measure.origo()}; } MeasureAxis::MeasureAxis(Math::Range interval, @@ -41,6 +42,8 @@ double MeasureAxis::origo() const return -range.getMin() / range.size(); } +void MeasureAxis::track(double value) { trackedRange.include(value); } + MeasureAxis interpolate(const MeasureAxis &op0, const MeasureAxis &op1, double factor) @@ -119,6 +122,11 @@ bool DimensionAxis::setLabels(double step) return hasLabel; } +void DimensionAxis::track(const Data::MarkerId &id) +{ + (*trackedValues)[id.itemId] = id.label; +} + DimensionAxis interpolate(const DimensionAxis &op0, const DimensionAxis &op1, double factor) diff --git a/src/chart/generator/axis.h b/src/chart/generator/axis.h index 43f9824fd..d43429f6b 100644 --- a/src/chart/generator/axis.h +++ b/src/chart/generator/axis.h @@ -36,14 +36,8 @@ template struct AbstractAxises : throw std::logic_error("not an axis channel"); } - bool operator==(const AbstractAxises &other) const - { - for (auto i = 0; i < std::size(axises); ++i) { - auto id = static_cast(i); - if (axises[id] != other.axises[id]) return false; - } - return true; - } + [[nodiscard]] bool operator==( + const AbstractAxises &other) const = default; }; struct CommonAxis @@ -53,14 +47,13 @@ struct CommonAxis [[nodiscard]] bool operator==(const CommonAxis &) const = default; }; -using CommonAxises = AbstractAxises; - CommonAxis interpolate(const CommonAxis &op0, const CommonAxis &op1, double factor); struct MeasureAxis { + Math::Range trackedRange; ::Anim::Interpolated enabled{false}; Math::Range range = Math::Range::Raw(0, 1); ::Anim::String unit; @@ -73,24 +66,20 @@ struct MeasureAxis std::optional step); bool operator==(const MeasureAxis &other) const; [[nodiscard]] double origo() const; + + void track(double value); }; MeasureAxis interpolate(const MeasureAxis &op0, const MeasureAxis &op1, double factor); -struct MeasureAxises : public AbstractAxises -{ - [[nodiscard]] Geom::Point origo() const; -}; - struct DimensionAxis { friend DimensionAxis interpolate(const DimensionAxis &op0, const DimensionAxis &op1, double factor); -public: class Item { public: @@ -140,6 +129,8 @@ struct DimensionAxis bool enabled{false}; std::string category{}; + std::shared_ptr>> + trackedValues; DimensionAxis() = default; bool add(const Data::SliceIndex &index, @@ -160,6 +151,7 @@ struct DimensionAxis return values.cend(); } bool setLabels(double step); + void track(const Data::MarkerId &id); private: Values values; @@ -169,7 +161,17 @@ DimensionAxis interpolate(const DimensionAxis &op0, const DimensionAxis &op1, double factor); -using DimensionAxises = AbstractAxises; +struct Axis +{ + CommonAxis common; + MeasureAxis measure; + DimensionAxis dimension; +}; + +struct Axises : AbstractAxises +{ + Geom::Point origo() const; +}; } diff --git a/src/chart/generator/channelstats.cpp b/src/chart/generator/channelstats.cpp deleted file mode 100644 index 61be97227..000000000 --- a/src/chart/generator/channelstats.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "channelstats.h" - -#include "dataframe/old/datatable.h" - -namespace Vizzu::Gen -{ - -ChannelStats::ChannelStats(const Channel &channel, - const Data::DataCube &cube) -{ - if (channel.isDimension()) - stat.emplace<1>( - cube.combinedSizeOf(channel.dimensions()).second); -} - -void ChannelStats::track(double value) { range().include(value); } - -void ChannelStats::track(const Marker::Id &id) -{ - indices()[id.itemId] = id.label; -} - -ChannelsStats::ChannelsStats(const Channels ¶mchannels, - const Data::DataCube &cube) : - channels( - [&]( - std::index_sequence) -> decltype(channels) - { - return {ChannelStats( - paramchannels.at(static_cast(Ix)), - cube)...}; - }(std::make_index_sequence< - std::tuple_size_v>{})) -{} - -} \ No newline at end of file diff --git a/src/chart/generator/channelstats.h b/src/chart/generator/channelstats.h deleted file mode 100644 index f5c277c48..000000000 --- a/src/chart/generator/channelstats.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef CHANNELSTATS_H -#define CHANNELSTATS_H - -#include - -#include "base/math/range.h" -#include "chart/options/channels.h" -#include "dataframe/old/types.h" - -#include "marker.h" - -namespace Vizzu::Gen -{ - -class ChannelStats -{ -public: - std::variant, - std::vector>> - stat; - - ChannelStats(const Channel &channel, const Data::DataCube &cube); - - void track(double value); - void track(const Marker::Id &id); - - Math::Range &range() - { - thread_local Math::Range dummy{}; - auto *range = std::get_if>(&stat); - return range ? *range : dummy; - } - - [[nodiscard]] std::vector> & - indices() - { - return *std::get_if<1>(&stat); - } -}; - -class ChannelsStats -{ -public: - ChannelsStats(const Channels ¶mchannels, - const Data::DataCube &cube); - - Refl::EnumArray channels; -}; - -} - -#endif diff --git a/src/chart/generator/guides.cpp b/src/chart/generator/guides.cpp index 6668a53ad..83179da26 100644 --- a/src/chart/generator/guides.cpp +++ b/src/chart/generator/guides.cpp @@ -30,7 +30,7 @@ bool GuidesByAxis::operator==(const GuidesByAxis &other) const && interlacings == other.interlacings; } -void Guides::init(const Options &options) +Guides::Guides(const Options &options) { auto isCircle = options.geometry.get() == ShapeType::circle; auto isLine = options.geometry.get() == ShapeType::line; diff --git a/src/chart/generator/guides.h b/src/chart/generator/guides.h index f00bc8c43..6f3738560 100644 --- a/src/chart/generator/guides.h +++ b/src/chart/generator/guides.h @@ -31,7 +31,7 @@ struct Guides GuidesByAxis x; GuidesByAxis y; - void init(const Options &options); + explicit Guides(const Options &options); [[nodiscard]] const GuidesByAxis &at(ChannelId channel) const; GuidesByAxis &at(ChannelId channel); [[nodiscard]] bool hasAnyGuides() const; diff --git a/src/chart/generator/marker.cpp b/src/chart/generator/marker.cpp index 98fffedca..db747fd44 100644 --- a/src/chart/generator/marker.cpp +++ b/src/chart/generator/marker.cpp @@ -2,14 +2,14 @@ #include "dataframe/old/datatable.h" -#include "channelstats.h" +#include "axis.h" namespace Vizzu::Gen { Marker::Marker(const Options &options, const Data::DataCube &data, - ChannelsStats &stats, + Axises &stats, const Data::MultiIndex &index, MarkerIndex idx, bool needMarkerInfo) : @@ -154,7 +154,7 @@ Conv::JSONObj &&Marker::appendToJSON(Conv::JSONObj &&jsonObj) const double Marker::getValueForChannel(const Channels &channels, ChannelId type, const Data::DataCube &data, - ChannelsStats &stats, + Axises &stats, const Data::MultiIndex &index, const Data::MarkerId *mid) const { @@ -164,7 +164,7 @@ double Marker::getValueForChannel(const Channels &channels, double value{}; - auto &stat = stats.channels[type]; + auto &stat = stats.at(type); if (channel.isDimension()) { std::optional nid; @@ -178,7 +178,7 @@ double Marker::getValueForChannel(const Channels &channels, else value = static_cast(id.itemId); - if (enabled) stat.track(id); + if (enabled) stat.dimension.track(id); } else { if (const auto &measure = *channel.measureId; @@ -187,7 +187,7 @@ double Marker::getValueForChannel(const Channels &channels, else value = data.valueAt(index, measure); - if (enabled) { stat.track(value); } + if (enabled) stat.measure.track(value); } return value; } diff --git a/src/chart/generator/marker.h b/src/chart/generator/marker.h index 050f82e08..a175f1d61 100644 --- a/src/chart/generator/marker.h +++ b/src/chart/generator/marker.h @@ -17,7 +17,7 @@ namespace Vizzu::Gen { -class ChannelsStats; +struct Axises; class Marker { @@ -26,7 +26,7 @@ class Marker Marker(const Options &options, const Data::DataCube &data, - ChannelsStats &stats, + Axises &stats, const Data::MultiIndex &index, MarkerIndex idx, bool needMarkerInfo); @@ -83,7 +83,7 @@ class Marker double getValueForChannel(const Channels &channels, ChannelId type, const Data::DataCube &data, - ChannelsStats &stats, + Axises &stats, const Data::MultiIndex &index, const Data::MarkerId * = nullptr) const; }; diff --git a/src/chart/generator/plot.cpp b/src/chart/generator/plot.cpp index afd3e8448..97296cbee 100644 --- a/src/chart/generator/plot.cpp +++ b/src/chart/generator/plot.cpp @@ -48,55 +48,12 @@ bool Plot::MarkerInfoContent::operator==( return markerId == op.markerId; } -Plot::Plot(PlotOptionsPtr options, const Plot &other) : - anyAxisSet(other.anyAxisSet), - commonAxises(other.commonAxises), - measureAxises(other.measureAxises), - guides(other.guides), - dimensionAxises(other.dimensionAxises), - keepAspectRatio(other.keepAspectRatio), - dataTable(other.dataTable), - options(std::move(options)), - style(other.style), - markers(other.markers), - markersInfo(other.markersInfo) -{} - -Plot::Plot(const Data::DataTable &dataTable, - PlotOptionsPtr opts, - Styles::Chart style) : - dataTable(dataTable), +Plot::Plot(PlotOptionsPtr opts, Styles::Chart style) : + anyAxisSet(opts->getChannels().anyAxisSet()), + guides(*opts), options(std::move(opts)), style(std::move(style)) -{ - Data::DataCube dataCube(dataTable, *options); - this->dataCube = &dataCube; - ChannelsStats stats(options->getChannels(), getDataCube()); - this->stats = &stats; - - anyAxisSet = options->getChannels().anyAxisSet(); - - auto &&subBuckets = generateMarkers(); - - if (!options->getChannels().anyAxisSet()) { - addSpecLayout(subBuckets); - calcDimensionAxises(); - normalizeColors(); - if (options->geometry != ShapeType::circle) normalizeSizes(); - calcMeasureAxises(); - } - else { - addSeparation(subBuckets); - normalizeXY(); - calcDimensionAxises(); - normalizeSizes(); - normalizeColors(); - calcMeasureAxises(); - addAlignment(subBuckets); - } - - guides.init(*options); -} +{} void Plot::detachOptions() { @@ -108,405 +65,6 @@ bool Plot::isEmpty() const return options->getChannels().isEmpty(); } -Buckets Plot::generateMarkers() -{ - Buckets mainBuckets; - Buckets subBuckets; - if (!getDataCube().empty()) { - mainBuckets.resize(getDataCube().combinedSizeOf( - options->mainAxis().dimensions())); - mainBucketSize = mainBuckets.size(); - - Data::SeriesList subIds(options->subAxis().dimensions()); - if (getOptions()->geometry == ShapeType::area) - subIds.split_by(options->mainAxis().dimensions()); - subBuckets.resize(getDataCube().combinedSizeOf(subIds)); - - markers.reserve(getDataCube().combinedSizeOf({}).first); - } - - std::multimap map; - for (auto &&[ix, mid] : options->markersInfo) - map.emplace(mid, ix); - - for (auto first = map.begin(), last = map.end(); - auto &&index : getDataCube()) { - auto &&markerId = markers.size(); - auto needInfo = first != last && first->first == markerId; - - auto &marker = markers.emplace_back(*options, - getDataCube(), - getStats(), - index, - markerId, - needInfo); - - mainBuckets[marker.mainId.get().seriesId] - [marker.mainId.get().itemId] = ▮ - subBuckets[marker.subId.seriesId][marker.subId.itemId] = - ▮ - - while (needInfo) { - markersInfo.insert({first++->second, - MarkerInfo{MarkerInfoContent{marker}}}); - needInfo = first != last && first->first == markerId; - } - } - clearEmptyBuckets(mainBuckets, true); - clearEmptyBuckets(subBuckets, false); - auto &&hasMarkerConnection = linkMarkers(mainBuckets, true); - [[maybe_unused]] auto &&_ = linkMarkers(subBuckets, false); - - if (hasMarkerConnection - && options->geometry.get() == ShapeType::line - && options->getChannels().at(ChannelId::x).isDimension() - && options->getChannels().at(ChannelId::y).isDimension()) { - markerConnectionOrientation.emplace( - *options->orientation.get()); - } - return subBuckets; -} - -std::vector> -Plot::sortedBuckets(const Buckets &buckets, bool main) const -{ - std::vector> sorted( - buckets.inner_size()); - - for (auto &&bucket : buckets) - for (std::size_t ix{}; auto &&marker : bucket) { - auto &[s, f] = sorted[ix]; - f = ix++; - s += marker->size.getCoord(!options->isHorizontal()); - } - - if (main && options->sort == Sort::byValue) - std::sort(sorted.begin(), sorted.end()); - - if (main && options->reverse) - std::reverse(sorted.begin(), sorted.end()); - - return sorted; -} - -void Plot::clearEmptyBuckets(const Buckets &buckets, bool main) const -{ - for (auto &&bucket : buckets) - if (!std::any_of(bucket.begin(), - bucket.end(), - std::mem_fn(&Marker::enabled))) - for (auto &&marker : bucket) - marker->resetSize(options->isHorizontal() == !main); -} - -void Plot::addSpecLayout(Buckets &buckets) -{ - auto geometry = - getOptions()->geometry.get_or_first(::Anim::first).value; - if (auto &markers = getMarkers(); - geometry == ShapeType::line || geometry == ShapeType::area) { - Charts::TableChart::setupVector(markers, true); - } - else if (auto &&size = - getOptions()->getChannels().at(ChannelId::size); - size.isEmpty()) { - Charts::TableChart::setupVector(markers); - } - else if (!getDataCube().empty()) { - buckets.resize( - getDataCube().combinedSizeOf(size.dimensions())); - - for (auto &marker : markers) - buckets[marker.sizeId.seriesId][marker.sizeId.itemId] = - ▮ - - if (geometry == ShapeType::circle) { - Charts::BubbleChartBuilder::setupVector( - *getStyle().plot.marker.circleMaxRadius, - buckets); - - keepAspectRatio = true; - } - else - Charts::TreeMap::setupVector(buckets); - } -} - -bool Plot::linkMarkers(const Buckets &buckets, bool main) const -{ - bool hasConnection{}; - for (auto &&sorted = sortedBuckets(buckets, main); - const auto &bucket : buckets) - for (auto i = 0U; i < sorted.size(); ++i) { - auto idAct = sorted[i].second; - auto &act = *bucket[idAct]; - auto iNext = (i + 1) % sorted.size(); - auto idNext = sorted[iNext].second; - auto &next = *bucket[idNext]; - act.setNextMarker(iNext == 0, - next, - options->isHorizontal() == main, - main); - if (act.enabled && next.enabled && idAct != idNext) - hasConnection = true; - } - return hasConnection; -} - -void Plot::normalizeXY() -{ - const auto &xrange = options->getHorizontalAxis().range; - const auto &yrange = options->getVeritalAxis().range; - - if (markers.empty()) { - getStats().channels[ChannelId::x].range() = - xrange.getRange({0.0, 0.0}); - getStats().channels[ChannelId::y].range() = - yrange.getRange({0.0, 0.0}); - return; - } - - auto boundRect = markers.front().toRectangle(); - - for (auto &marker : markers) { - if (!marker.enabled) continue; - boundRect = boundRect.boundary(marker.toRectangle()); - } - - options->setAutoRange(boundRect.positive().hSize().getMin() >= 0, - boundRect.positive().vSize().getMin() >= 0); - - boundRect.setHSize(xrange.getRange(boundRect.hSize())); - boundRect.setVSize(yrange.getRange(boundRect.vSize())); - - for (auto &marker : markers) { - if (!boundRect.intersects(marker.toRectangle().positive())) - marker.enabled = false; - - auto rect = marker.toRectangle(); - auto newRect = boundRect.normalize(rect); - marker.fromRectangle(newRect); - } - - getStats().channels[ChannelId::x].range() = boundRect.hSize(); - getStats().channels[ChannelId::y].range() = boundRect.vSize(); -} - -void Plot::calcMeasureAxises() -{ - for (auto i = 0U; i < std::size(measureAxises.axises); ++i) - calcMeasureAxis(static_cast(i)); -} - -void Plot::calcMeasureAxis(ChannelId type) -{ - auto &axis = measureAxises.at(type); - const auto &scale = options->getChannels().at(type); - if (auto &&meas = scale.measureId) { - commonAxises.at(type).title = scale.title.isAuto() - ? dataCube->getName(*meas) - : scale.title ? *scale.title - : std::string{}; - - if (type == options->subAxisType() - && options->align == Base::Align::Type::stretch) { - axis = {Math::Range::Raw(0, 100), - "%", - meas->getColIndex(), - scale.step.getValue()}; - } - else { - auto range = getStats().channels[type].range(); - if (!range.isReal()) - range = Math::Range::Raw({}, {}); - - axis = {range, - dataTable.getUnit(meas->getColIndex()), - meas->getColIndex(), - scale.step.getValue()}; - } - } - else - axis = {}; -} - -void Plot::calcDimensionAxises() -{ - for (auto i = 0U; i < std::size(dimensionAxises.axises); ++i) - calcDimensionAxis(static_cast(i)); -} - -void Plot::calcDimensionAxis(ChannelId type) -{ - auto &axis = dimensionAxises.at(type); - auto &scale = options->getChannels().at(type); - - if (scale.isMeasure() || !scale.hasDimension()) return; - - auto &&isTypeAxis = isAxis(type); - if (auto merge = scale.labelLevel == 0; isTypeAxis) { - for (const auto &marker : markers) { - const auto &id = - (type == ChannelId::x) == options->isHorizontal() - ? marker.mainId.get() - : marker.subId; - - if (const auto &slice = id.label) - axis.add(*slice, - static_cast(id.itemId), - marker.getSizeBy(type == ChannelId::x), - static_cast(marker.enabled), - merge); - } - } - else { - const auto &indices = getStats().channels[type].indices(); - - double count = 0; - for (auto i = 0U; i < indices.size(); ++i) - if (const auto &sliceIndex = indices[i]; - sliceIndex - && axis.add(*sliceIndex, - i, - {count, count}, - true, - merge)) - count += 1; - } - auto hasLabel = - axis.setLabels(isTypeAxis ? scale.step.getValue(1.0) : 1.0); - - if (auto &&series = scale.labelSeries()) - axis.category = series.value().getColIndex(); - - commonAxises.at(type).title = scale.title.isAuto() && !hasLabel - ? axis.category - : scale.title ? *scale.title - : std::string{}; -} - -void Plot::addAlignment(const Buckets &subBuckets) const -{ - if (static_cast(options->split)) return; - - if (measureAxises.at(options->subAxisType()).range.getMin() < 0) - return; - - if (options->align == Base::Align::Type::none) return; - - auto &&vectical = !options->isHorizontal(); - const Base::Align align{options->align, Math::Range(0.0, 1.0)}; - for (auto &&bucket : subBuckets) { - Math::Range range; - - for (auto &&marker : bucket) - range.include(marker->getSizeBy(vectical)); - - auto &&transform = align.getAligned(range) / range; - - for (auto &&marker : bucket) - marker->setSizeBy(vectical, - marker->getSizeBy(vectical) * transform); - } -} - -void Plot::addSeparation(const Buckets &subBuckets) const -{ - if (static_cast(options->split)) { - auto align = options->align == Base::Align::Type::none - ? Base::Align::Type::min - : options->align; - - std::vector ranges{mainBucketSize, - Math::Range::Raw({}, {})}; - std::vector anyEnabled(mainBucketSize); - - auto &&vertical = !options->isHorizontal(); - for (auto &&bucket : subBuckets) - for (auto i = 0U; auto &&marker : bucket) { - ranges[i].include(marker->getSizeBy(vertical).size()); - if (static_cast(marker->enabled) > 0) - anyEnabled[i] = true; - ++i %= ranges.size(); - } - - auto max = Math::Range(0.0, 0.0); - for (auto i = 1U; i < ranges.size(); ++i) - if (anyEnabled[i]) max = max + ranges[i]; - - for (auto i = 1U; i < ranges.size(); ++i) - ranges[i] = ranges[i] + ranges[i - 1].getMax() - + (anyEnabled[i - 1] ? max.getMax() / 15 : 0); - - for (auto &&bucket : subBuckets) - for (auto i = 0U; auto &&marker : bucket) - marker->setSizeBy(vertical, - Base::Align{align, ranges[(i %= ranges.size())++]} - .getAligned(marker->getSizeBy(vertical))); - } -} - -void Plot::normalizeSizes() -{ - if (options->geometry == ShapeType::circle - || options->geometry == ShapeType::line) { - Math::Range size; - - for (auto &marker : markers) - if (marker.enabled) size.include(marker.sizeFactor); - - size = options->getChannels() - .at(ChannelId::size) - .range.getRange(size); - - for (auto &marker : markers) - marker.sizeFactor = size.getMax() == size.getMin() - ? 0 - : size.normalize(marker.sizeFactor); - } - else - for (auto &marker : markers) marker.sizeFactor = 0; -} - -void Plot::normalizeColors() -{ - Math::Range lightness; - Math::Range color; - - for (auto &marker : markers) { - auto &&cbase = marker.colorBase.get(); - if (!cbase.isDiscrete()) color.include(cbase.getPos()); - lightness.include(cbase.getLightness()); - } - - color = options->getChannels() - .at(ChannelId::color) - .range.getRange(color); - lightness = options->getChannels() - .at(ChannelId::lightness) - .range.getRange(lightness); - - for (auto &marker : markers) { - auto &&cbase = marker.colorBase->value; - cbase.setLightness(lightness.rescale(cbase.getLightness())); - - if (!cbase.isDiscrete()) - cbase.setPos(color.rescale(cbase.getPos())); - } - - getStats().channels[ChannelId::color].range() = color; - getStats().channels[ChannelId::lightness].range() = lightness; - - for (auto &value : dimensionAxises.at(ChannelId::color)) - value.second.colorBase = - ColorBase(static_cast(value.second.value), 0.5); - - for (auto &value : dimensionAxises.at(ChannelId::lightness)) { - value.second.value = lightness.rescale(value.second.value); - value.second.colorBase = ColorBase(0U, value.second.value); - } -} - void Plot::prependMarkers(const Plot &plot) { auto size = plot.markers.size(); diff --git a/src/chart/generator/plot.h b/src/chart/generator/plot.h index e66c4ec13..0d190e31a 100644 --- a/src/chart/generator/plot.h +++ b/src/chart/generator/plot.h @@ -10,7 +10,6 @@ #include "dataframe/old/datatable.h" #include "axis.h" -#include "channelstats.h" #include "guides.h" #include "marker.h" #include "plotptr.h" @@ -30,13 +29,11 @@ class AbstractMorph; namespace Gen { -class Selector; - class Plot { friend class Anim::Keyframe; friend class Anim::Morph::AbstractMorph; - friend class Selector; + friend class PlotBuilder; public: using Markers = std::vector; @@ -61,18 +58,13 @@ class Plot static bool dimensionMatch(const Plot &a, const Plot &b); Math::FuzzyBool anyAxisSet; - CommonAxises commonAxises; - MeasureAxises measureAxises; + Axises axises; Guides guides; - DimensionAxises dimensionAxises; Math::FuzzyBool keepAspectRatio; std::optional markerConnectionOrientation; Plot(const Plot &other) = default; - Plot(PlotOptionsPtr options, const Plot &other); - Plot(const Data::DataTable &dataTable, - PlotOptionsPtr opts, - Styles::Chart style); + Plot(PlotOptionsPtr opts, Styles::Chart style); [[nodiscard]] const Markers &getMarkers() const { return markers; @@ -94,46 +86,14 @@ class Plot return style; } Styles::Chart &getStyle() { return style; } - [[nodiscard]] const Data::DataTable &getTable() const - { - return dataTable; - }; void detachOptions(); [[nodiscard]] bool isEmpty() const; private: - const Data::DataTable &dataTable; PlotOptionsPtr options; Styles::Chart style; Markers markers; MarkersInfo markersInfo; - - Data::DataCube *dataCube{}; - ChannelsStats *stats{}; - std::size_t mainBucketSize{}; - - [[nodiscard]] const Data::DataCube &getDataCube() const - { - return *dataCube; - } - - [[nodiscard]] ChannelsStats &getStats() { return *stats; } - Buckets generateMarkers(); - [[nodiscard]] bool linkMarkers(const Buckets &buckets, - bool main) const; - void normalizeXY(); - void calcMeasureAxises(); - void calcMeasureAxis(ChannelId type); - void calcDimensionAxises(); - void calcDimensionAxis(ChannelId type); - void addAlignment(const Buckets &subBuckets) const; - void addSeparation(const Buckets &subBuckets) const; - void normalizeSizes(); - void normalizeColors(); - [[nodiscard]] std::vector> - sortedBuckets(const Buckets &buckets, bool main) const; - void clearEmptyBuckets(const Buckets &buckets, bool main) const; - void addSpecLayout(Buckets &buckets); }; struct PlotParent diff --git a/src/chart/generator/plotbuilder.cpp b/src/chart/generator/plotbuilder.cpp new file mode 100644 index 000000000..6fe1ebb5a --- /dev/null +++ b/src/chart/generator/plotbuilder.cpp @@ -0,0 +1,482 @@ +#include "plotbuilder.h" + +#include +#include +#include + +#include "plot.h" + +namespace Vizzu::Gen +{ + +PlotBuilder::PlotBuilder(const Data::DataTable &dataTable, + const PlotOptionsPtr &options, + const Styles::Chart &style) : + dataCube(dataTable, *options), + plot(std::make_shared(options, style)) +{ + for (const Channel &ch : + plot->options->getChannels().getChannels()) + if (ch.isDimension()) + plot->axises.at(ch.type).dimension.trackedValues = + std::make_shared< + std::vector>>( + dataCube.combinedSizeOf(ch.dimensions()).second); + std::size_t mainBucketSize{}; + auto &&subBuckets = generateMarkers(mainBucketSize); + + if (!plot->options->getChannels().anyAxisSet()) { + addSpecLayout(subBuckets); + calcDimensionAxises(); + normalizeColors(); + if (plot->options->geometry != ShapeType::circle) + normalizeSizes(); + calcMeasureAxises(dataTable); + } + else { + addSeparation(subBuckets, mainBucketSize); + normalizeXY(); + calcDimensionAxises(); + normalizeSizes(); + normalizeColors(); + calcMeasureAxises(dataTable); + addAlignment(subBuckets); + } + + for (const Channel &ch : + plot->options->getChannels().getChannels()) + if (ch.isDimension()) + plot->axises.at(ch.type).dimension.trackedValues.reset(); +} + +Buckets PlotBuilder::generateMarkers(std::size_t &mainBucketSize) +{ + Buckets mainBuckets; + Buckets subBuckets; + if (!dataCube.empty()) { + mainBuckets.resize(dataCube.combinedSizeOf( + plot->getOptions()->mainAxis().dimensions())); + mainBucketSize = mainBuckets.size(); + + Data::SeriesList subIds( + plot->getOptions()->subAxis().dimensions()); + if (plot->getOptions()->geometry == ShapeType::area) + subIds.split_by( + plot->getOptions()->mainAxis().dimensions()); + subBuckets.resize(dataCube.combinedSizeOf(subIds)); + + plot->markers.reserve(dataCube.combinedSizeOf({}).first); + } + + std::multimap map; + for (auto &&[ix, mid] : plot->getOptions()->markersInfo) + map.emplace(mid, ix); + + for (auto first = map.begin(), last = map.end(); + auto &&index : dataCube) { + auto &&markerId = plot->markers.size(); + auto needInfo = first != last && first->first == markerId; + + auto &marker = plot->markers.emplace_back(*plot->getOptions(), + dataCube, + plot->axises, + index, + markerId, + needInfo); + + mainBuckets[marker.mainId.get().seriesId] + [marker.mainId.get().itemId] = ▮ + subBuckets[marker.subId.seriesId][marker.subId.itemId] = + ▮ + + while (needInfo) { + plot->markersInfo.insert({first++->second, + Plot::MarkerInfo{Plot::MarkerInfoContent{marker}}}); + needInfo = first != last && first->first == markerId; + } + } + clearEmptyBuckets(mainBuckets, true); + clearEmptyBuckets(subBuckets, false); + auto &&hasMarkerConnection = linkMarkers(mainBuckets, true); + [[maybe_unused]] auto &&_ = linkMarkers(subBuckets, false); + + if (hasMarkerConnection + && plot->getOptions()->geometry.get() == ShapeType::line + && plot->getOptions() + ->getChannels() + .at(ChannelId::x) + .isDimension() + && plot->getOptions() + ->getChannels() + .at(ChannelId::y) + .isDimension()) { + plot->markerConnectionOrientation.emplace( + *plot->getOptions()->orientation.get()); + } + return subBuckets; +} + +std::vector> +PlotBuilder::sortedBuckets(const Buckets &buckets, bool main) const +{ + std::vector> sorted( + buckets.inner_size()); + + for (auto &&bucket : buckets) + for (std::size_t ix{}; auto &&marker : bucket) { + auto &[s, f] = sorted[ix]; + f = ix++; + s += marker->size.getCoord( + !plot->getOptions()->isHorizontal()); + } + + if (main && plot->getOptions()->sort == Sort::byValue) + std::sort(sorted.begin(), sorted.end()); + + if (main && plot->getOptions()->reverse) + std::reverse(sorted.begin(), sorted.end()); + + return sorted; +} + +void PlotBuilder::clearEmptyBuckets(const Buckets &buckets, + bool main) const +{ + for (auto &&bucket : buckets) + if (!std::any_of(bucket.begin(), + bucket.end(), + std::mem_fn(&Marker::enabled))) + for (auto &&marker : bucket) + marker->resetSize( + plot->getOptions()->isHorizontal() == !main); +} + +void PlotBuilder::addSpecLayout(Buckets &buckets) +{ + auto geometry = plot->getOptions() + ->geometry.get_or_first(::Anim::first) + .value; + if (auto &markers = plot->markers; + geometry == ShapeType::line || geometry == ShapeType::area) { + Charts::TableChart::setupVector(markers, true); + } + else if (auto &&size = plot->getOptions()->getChannels().at( + ChannelId::size); + size.isEmpty()) { + Charts::TableChart::setupVector(markers); + } + else if (!dataCube.empty()) { + buckets.resize(dataCube.combinedSizeOf(size.dimensions())); + + for (auto &marker : markers) + buckets[marker.sizeId.seriesId][marker.sizeId.itemId] = + ▮ + + if (geometry == ShapeType::circle) { + Charts::BubbleChartBuilder::setupVector( + *plot->getStyle().plot.marker.circleMaxRadius, + buckets); + + plot->keepAspectRatio = true; + } + else + Charts::TreeMap::setupVector(buckets); + } +} + +Math::Range &PlotBuilder::getMeasTrackRange( + ChannelId type) const +{ + return plot->axises.at(type).measure.trackedRange; +} + +bool PlotBuilder::linkMarkers(const Buckets &buckets, bool main) const +{ + bool hasConnection{}; + for (auto &&sorted = sortedBuckets(buckets, main); + const auto &bucket : buckets) + for (auto i = 0U; i < sorted.size(); ++i) { + auto idAct = sorted[i].second; + auto &act = *bucket[idAct]; + auto iNext = (i + 1) % sorted.size(); + auto idNext = sorted[iNext].second; + auto &next = *bucket[idNext]; + act.setNextMarker(iNext == 0, + next, + plot->getOptions()->isHorizontal() == main, + main); + if (act.enabled && next.enabled && idAct != idNext) + hasConnection = true; + } + return hasConnection; +} + +void PlotBuilder::normalizeXY() +{ + const auto &xrange = + plot->getOptions()->getHorizontalAxis().range; + const auto &yrange = plot->getOptions()->getVeritalAxis().range; + + if (plot->markers.empty()) { + getMeasTrackRange(ChannelId::x) = xrange.getRange({0.0, 0.0}); + getMeasTrackRange(ChannelId::y) = yrange.getRange({0.0, 0.0}); + return; + } + + auto boundRect = plot->markers.front().toRectangle(); + + for (auto &marker : plot->markers) { + if (!marker.enabled) continue; + boundRect = boundRect.boundary(marker.toRectangle()); + } + + plot->getOptions()->setAutoRange( + boundRect.positive().hSize().getMin() >= 0, + boundRect.positive().vSize().getMin() >= 0); + + boundRect.setHSize(xrange.getRange(boundRect.hSize())); + boundRect.setVSize(yrange.getRange(boundRect.vSize())); + + for (auto &marker : plot->markers) { + if (!boundRect.intersects(marker.toRectangle().positive())) + marker.enabled = false; + + auto rect = marker.toRectangle(); + auto newRect = boundRect.normalize(rect); + marker.fromRectangle(newRect); + } + + getMeasTrackRange(ChannelId::x) = boundRect.hSize(); + getMeasTrackRange(ChannelId::y) = boundRect.vSize(); +} + +void PlotBuilder::calcMeasureAxises(const Data::DataTable &dataTable) +{ + for (const Channel &ch : + plot->getOptions()->getChannels().getChannels()) + calcMeasureAxis(dataTable, ch.type); +} + +void PlotBuilder::calcMeasureAxis(const Data::DataTable &dataTable, + ChannelId type) +{ + auto &axis = plot->axises.at(type).measure; + const auto &scale = plot->getOptions()->getChannels().at(type); + auto range = getMeasTrackRange(type); + if (auto &&meas = scale.measureId) { + plot->axises.at(type).common.title = + scale.title.isAuto() ? dataCube.getName(*meas) + : scale.title ? *scale.title + : std::string{}; + + if (type == plot->getOptions()->subAxisType() + && plot->getOptions()->align + == Base::Align::Type::stretch) { + axis = {Math::Range::Raw(0, 100), + "%", + meas->getColIndex(), + scale.step.getValue()}; + } + else { + axis = {range.isReal() ? range + : Math::Range::Raw(0, 0), + dataTable.getUnit(meas->getColIndex()), + meas->getColIndex(), + scale.step.getValue()}; + } + } + else + axis = {}; + axis.trackedRange = range; +} + +void PlotBuilder::calcDimensionAxises() +{ + for (const Channel &ch : + plot->getOptions()->getChannels().getChannels()) + calcDimensionAxis(ch.type); +} + +void PlotBuilder::calcDimensionAxis(ChannelId type) +{ + auto &axis = plot->axises.at(type).dimension; + auto &scale = plot->getOptions()->getChannels().at(type); + + if (scale.isMeasure() || !scale.hasDimension()) return; + + auto &&isTypeAxis = isAxis(type); + if (auto merge = scale.labelLevel == 0; isTypeAxis) { + for (const auto &marker : plot->markers) { + const auto &id = + (type == ChannelId::x) + == plot->getOptions()->isHorizontal() + ? marker.mainId.get() + : marker.subId; + + if (const auto &slice = id.label) + axis.add(*slice, + static_cast(id.itemId), + marker.getSizeBy(type == ChannelId::x), + static_cast(marker.enabled), + merge); + } + } + else { + const auto &indices = + *plot->axises.at(type).dimension.trackedValues; + + double count = 0; + for (auto i = 0U; i < indices.size(); ++i) + if (const auto &sliceIndex = indices[i]; + sliceIndex + && axis.add(*sliceIndex, + i, + {count, count}, + true, + merge)) + count += 1; + } + auto hasLabel = + axis.setLabels(isTypeAxis ? scale.step.getValue(1.0) : 1.0); + + if (auto &&series = scale.labelSeries()) + axis.category = series.value().getColIndex(); + + plot->axises.at(type).common.title = + scale.title.isAuto() && !hasLabel ? axis.category + : scale.title ? *scale.title + : std::string{}; +} + +void PlotBuilder::addAlignment(const Buckets &subBuckets) const +{ + if (static_cast(plot->getOptions()->split)) return; + + if (plot->axises.at(plot->getOptions()->subAxisType()) + .measure.range.getMin() + < 0) + return; + + if (plot->getOptions()->align == Base::Align::Type::none) return; + + auto &&vectical = !plot->getOptions()->isHorizontal(); + const Base::Align align{plot->getOptions()->align, + Math::Range(0.0, 1.0)}; + for (auto &&bucket : subBuckets) { + Math::Range range; + + for (auto &&marker : bucket) + range.include(marker->getSizeBy(vectical)); + + auto &&transform = align.getAligned(range) / range; + + for (auto &&marker : bucket) + marker->setSizeBy(vectical, + marker->getSizeBy(vectical) * transform); + } +} + +void PlotBuilder::addSeparation(const Buckets &subBuckets, + const std::size_t &mainBucketSize) const +{ + if (static_cast(plot->getOptions()->split)) { + auto align = + plot->getOptions()->align == Base::Align::Type::none + ? Base::Align::Type::min + : plot->getOptions()->align; + + std::vector ranges{mainBucketSize, + Math::Range::Raw({}, {})}; + std::vector anyEnabled(mainBucketSize); + + auto &&vertical = !plot->getOptions()->isHorizontal(); + for (auto &&bucket : subBuckets) + for (auto i = 0U; auto &&marker : bucket) { + ranges[i].include(marker->getSizeBy(vertical).size()); + if (static_cast(marker->enabled) > 0) + anyEnabled[i] = true; + ++i %= ranges.size(); + } + + auto max = Math::Range(0.0, 0.0); + for (auto i = 1U; i < ranges.size(); ++i) + if (anyEnabled[i]) max = max + ranges[i]; + + for (auto i = 1U; i < ranges.size(); ++i) + ranges[i] = ranges[i] + ranges[i - 1].getMax() + + (anyEnabled[i - 1] ? max.getMax() / 15 : 0); + + for (auto &&bucket : subBuckets) + for (auto i = 0U; auto &&marker : bucket) + marker->setSizeBy(vertical, + Base::Align{align, ranges[(i %= ranges.size())++]} + .getAligned(marker->getSizeBy(vertical))); + } +} + +void PlotBuilder::normalizeSizes() +{ + if (plot->getOptions()->geometry == ShapeType::circle + || plot->getOptions()->geometry == ShapeType::line) { + Math::Range size; + + for (auto &marker : plot->markers) + if (marker.enabled) size.include(marker.sizeFactor); + + size = plot->getOptions() + ->getChannels() + .at(ChannelId::size) + .range.getRange(size); + + for (auto &marker : plot->markers) + marker.sizeFactor = size.getMax() == size.getMin() + ? 0 + : size.normalize(marker.sizeFactor); + } + else + for (auto &marker : plot->markers) marker.sizeFactor = 0; +} + +void PlotBuilder::normalizeColors() +{ + Math::Range lightness; + Math::Range color; + + for (auto &marker : plot->markers) { + auto &&cbase = marker.colorBase.get(); + if (!cbase.isDiscrete()) color.include(cbase.getPos()); + lightness.include(cbase.getLightness()); + } + + color = plot->getOptions() + ->getChannels() + .at(ChannelId::color) + .range.getRange(color); + lightness = plot->getOptions() + ->getChannels() + .at(ChannelId::lightness) + .range.getRange(lightness); + + for (auto &marker : plot->markers) { + auto &&cbase = marker.colorBase->value; + cbase.setLightness(lightness.rescale(cbase.getLightness())); + + if (!cbase.isDiscrete()) + cbase.setPos(color.rescale(cbase.getPos())); + } + + getMeasTrackRange(ChannelId::color) = color; + getMeasTrackRange(ChannelId::lightness) = lightness; + + for (auto &value : plot->axises.at(ChannelId::color).dimension) + value.second.colorBase = + ColorBase(static_cast(value.second.value), 0.5); + + for (auto &value : + plot->axises.at(ChannelId::lightness).dimension) { + value.second.value = lightness.rescale(value.second.value); + value.second.colorBase = ColorBase(0U, value.second.value); + } +} + +} \ No newline at end of file diff --git a/src/chart/generator/plotbuilder.h b/src/chart/generator/plotbuilder.h new file mode 100644 index 000000000..2c3a8f3fb --- /dev/null +++ b/src/chart/generator/plotbuilder.h @@ -0,0 +1,48 @@ +#ifndef PLOTBUILDER_H +#define PLOTBUILDER_H + +#include "chart/main/style.h" +#include "chart/options/options.h" +#include "dataframe/old/datatable.h" + +#include "plotptr.h" + +namespace Vizzu::Gen +{ +class PlotBuilder +{ +public: + PlotBuilder(const Data::DataTable &dataTable, + const PlotOptionsPtr &opts, + const Styles::Chart &style); + + PlotPtr &&build() && { return std::move(plot); } + +private: + Data::DataCube dataCube; + PlotPtr plot; + + Buckets generateMarkers(std::size_t &mainBucketSize); + [[nodiscard]] bool linkMarkers(const Buckets &buckets, + bool main) const; + void normalizeXY(); + void calcMeasureAxises(const Data::DataTable &dataTable); + void calcMeasureAxis(const Data::DataTable &dataTable, + ChannelId type); + void calcDimensionAxises(); + void calcDimensionAxis(ChannelId type); + void addAlignment(const Buckets &subBuckets) const; + void addSeparation(const Buckets &subBuckets, + const std::size_t &mainBucketSize) const; + void normalizeSizes(); + void normalizeColors(); + [[nodiscard]] std::vector> + sortedBuckets(const Buckets &buckets, bool main) const; + void clearEmptyBuckets(const Buckets &buckets, bool main) const; + void addSpecLayout(Buckets &buckets); + + Math::Range &getMeasTrackRange(ChannelId type) const; +}; +} + +#endif // PLOTBUILDER_H diff --git a/src/chart/main/chart.cpp b/src/chart/main/chart.cpp index e0f980b26..34e81b56c 100644 --- a/src/chart/main/chart.cpp +++ b/src/chart/main/chart.cpp @@ -1,5 +1,7 @@ #include "chart.h" +#include + #include "chart/generator/plot.h" #include "chart/rendering/drawchart.h" @@ -11,7 +13,9 @@ Chart::Chart() : stylesheet(Styles::Chart::def(), actStyles), computedStyles(stylesheet.getDefaultParams()), events(eventDispatcher), - animator(*events.animation.begin, *events.animation.complete) + animator(table, + *events.animation.begin, + *events.animation.complete) { animator.onDraw.attach( [this](const Gen::PlotPtr &actPlot) @@ -108,9 +112,10 @@ Gen::PlotPtr Chart::plot(const Gen::PlotOptionsPtr &options) { options->setAutoParameters(); - auto res = std::make_shared(table, + auto res = Gen::PlotBuilder{table, options, - stylesheet.getFullParams(options, layout.boundary.size)); + stylesheet.getFullParams(options, layout.boundary.size)} + .build(); Styles::Sheet::setAfterStyles(*res, layout.boundary.size); diff --git a/src/chart/main/stylesheet.cpp b/src/chart/main/stylesheet.cpp index 8ba512db8..0fcfd8a47 100644 --- a/src/chart/main/stylesheet.cpp +++ b/src/chart/main/stylesheet.cpp @@ -194,7 +194,7 @@ void Sheet::setAfterStyles(Gen::Plot &plot, const Geom::Size &size) std::vector> ranges; bool has_collision = false; for (const auto &pair : - plot.dimensionAxises.at(Gen::ChannelId::x)) { + plot.axises.at(Gen::ChannelId::x).dimension) { if (pair.second.weight == 0) continue; diff --git a/src/chart/options/channels.h b/src/chart/options/channels.h index c72eb76b6..57c929839 100644 --- a/src/chart/options/channels.h +++ b/src/chart/options/channels.h @@ -44,6 +44,8 @@ class Channels [[nodiscard]] Channels shadow() const; + [[nodiscard]] auto &getChannels() { return channels; } + private: Refl::EnumArray channels = [](std::index_sequence) diff --git a/src/chart/rendering/drawaxes.cpp b/src/chart/rendering/drawaxes.cpp index 2f5f37717..b056cfea2 100644 --- a/src/chart/rendering/drawaxes.cpp +++ b/src/chart/rendering/drawaxes.cpp @@ -35,7 +35,7 @@ Geom::Line DrawAxes::getAxis(Gen::ChannelId axisIndex) const { auto horizontal = axisIndex == Gen::ChannelId::x; - auto offset = plot->measureAxises.other(axisIndex).origo(); + auto offset = plot->axises.other(axisIndex).measure.origo(); auto direction = Geom::Point::Ident(horizontal); @@ -95,7 +95,7 @@ Geom::Point DrawAxes::getTitleBasePos(Gen::ChannelId axisIndex, case Pos::min_edge: break; case Pos::max_edge: orthogonal = 1.0; break; case Pos::axis: - orthogonal = plot->measureAxises.other(axisIndex).origo(); + orthogonal = plot->axises.other(axisIndex).measure.origo(); break; } @@ -156,7 +156,7 @@ Geom::Point DrawAxes::getTitleOffset(Gen::ChannelId axisIndex, void DrawAxes::drawTitle(Gen::ChannelId axisIndex) const { - const auto &titleString = plot->commonAxises.at(axisIndex).title; + const auto &titleString = plot->axises.at(axisIndex).common.title; const auto &titleStyle = rootStyle.plot.getAxis(axisIndex).title; @@ -254,9 +254,9 @@ void DrawAxes::drawDimensionLabels(bool horizontal) const auto textColor = *labelStyle.color; if (textColor.alpha == 0.0) return; - auto origo = plot->measureAxises.origo(); - const auto &axises = plot->dimensionAxises; - const auto &axis = axises.at(axisIndex); + auto origo = plot->axises.origo(); + const auto &axises = plot->axises; + const auto &axis = axises.at(axisIndex).dimension; if (axis.enabled) { canvas.setFont(Gfx::Font{labelStyle}); diff --git a/src/chart/rendering/drawguides.cpp b/src/chart/rendering/drawguides.cpp index ed2361708..07ff5868a 100644 --- a/src/chart/rendering/drawguides.cpp +++ b/src/chart/rendering/drawguides.cpp @@ -20,8 +20,8 @@ void DrawGuides::draw(bool horizontal) auto baseColor = *guideStyle.color; if (baseColor.alpha == 0) return; - const auto &axises = plot->dimensionAxises; - const auto &axis = axises.at(axisId); + const auto &axises = plot->axises; + const auto &axis = axises.at(axisId).dimension; if (axis.enabled && *guideStyle.lineWidth > 0 && (static_cast(plot->guides.at(axisId).axisGuides) diff --git a/src/chart/rendering/drawinterlacing.cpp b/src/chart/rendering/drawinterlacing.cpp index 0754bcac1..dbec69a94 100644 --- a/src/chart/rendering/drawinterlacing.cpp +++ b/src/chart/rendering/drawinterlacing.cpp @@ -33,7 +33,7 @@ void DrawInterlacing::draw(bool horizontal, bool text) const if (!text && interlacingColor.alpha <= 0.0) return; - const auto &axis = plot->measureAxises.at(axisIndex); + const auto &axis = plot->axises.at(axisIndex).measure; if (!axis.range.isReal()) return; @@ -101,9 +101,9 @@ void DrawInterlacing::draw( const auto &axisStyle = rootStyle.plot.getAxis(axisIndex); - const auto &axis = plot->measureAxises.at(axisIndex); + const auto &axis = plot->axises.at(axisIndex).measure; - const auto origo = plot->measureAxises.origo(); + const auto origo = plot->axises.origo(); if (static_cast(enabled.interlacings || enabled.axisSticks || enabled.labels) diff --git a/src/chart/rendering/drawlegend.cpp b/src/chart/rendering/drawlegend.cpp index 1a1f89d9c..dec1528de 100644 --- a/src/chart/rendering/drawlegend.cpp +++ b/src/chart/rendering/drawlegend.cpp @@ -26,8 +26,8 @@ void DrawLegend::draw(Gfx::ICanvas &canvas, .titleHeight = style.title.getHeight(), .markerSize = style.marker.size->get(contentRect.size.y, style.label.calculatedSize()), - .measure = plot->measureAxises.at(channelType), - .dimension = plot->dimensionAxises.at(channelType), + .measure = plot->axises.at(channelType).measure, + .dimension = plot->axises.at(channelType).dimension, }; DrawBackground{{ctx()}}.draw(canvas, @@ -52,7 +52,7 @@ void DrawLegend::drawTitle(const Info &info) const { auto rect = info.contentRect; rect.size.y = info.titleHeight; - plot->commonAxises.at(info.type).title.visit( + plot->axises.at(info.type).common.title.visit( [this, &info, &rect, diff --git a/src/chart/rendering/markerrenderer.cpp b/src/chart/rendering/markerrenderer.cpp index ba6f88259..55efcc564 100644 --- a/src/chart/rendering/markerrenderer.cpp +++ b/src/chart/rendering/markerrenderer.cpp @@ -25,7 +25,7 @@ void MarkerRenderer::drawLines(Gfx::ICanvas &canvas, canvas.setLineWidth(*style.lineWidth); - auto origo = plot->measureAxises.origo(); + auto origo = plot->axises.origo(); auto baseColor = *style.color * double{plot->anyAxisSet}; @@ -178,7 +178,7 @@ void MarkerRenderer::drawMarkers(Gfx::ICanvas &canvas, void MarkerRenderer::drawLabels(Gfx::ICanvas &canvas) const { - auto &&axis = plot->measureAxises.at(Gen::ChannelId::label); + auto &&axis = plot->axises.at(Gen::ChannelId::label).measure; auto &&keepMeasure = !axis.origMeasureName.interpolates(); for (const auto &blended : markers) { if (blended.marker.enabled == false) continue; From c4d3f90e8356bbbb55136ba9ad57116bb7debb7d Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Fri, 17 May 2024 18:10:57 +0200 Subject: [PATCH 2/4] clang-tidy --- src/chart/generator/axis.h | 2 +- src/chart/generator/plotbuilder.h | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/chart/generator/axis.h b/src/chart/generator/axis.h index d43429f6b..70b59a16e 100644 --- a/src/chart/generator/axis.h +++ b/src/chart/generator/axis.h @@ -170,7 +170,7 @@ struct Axis struct Axises : AbstractAxises { - Geom::Point origo() const; + [[nodiscard]] Geom::Point origo() const; }; } diff --git a/src/chart/generator/plotbuilder.h b/src/chart/generator/plotbuilder.h index 2c3a8f3fb..b50b553f8 100644 --- a/src/chart/generator/plotbuilder.h +++ b/src/chart/generator/plotbuilder.h @@ -13,7 +13,7 @@ class PlotBuilder { public: PlotBuilder(const Data::DataTable &dataTable, - const PlotOptionsPtr &opts, + const PlotOptionsPtr &options, const Styles::Chart &style); PlotPtr &&build() && { return std::move(plot); } @@ -41,7 +41,8 @@ class PlotBuilder void clearEmptyBuckets(const Buckets &buckets, bool main) const; void addSpecLayout(Buckets &buckets); - Math::Range &getMeasTrackRange(ChannelId type) const; + [[nodiscard]] Math::Range &getMeasTrackRange( + ChannelId type) const; }; } From 05a8f1ded1d7d9fce56c7bdfc18c178d1f414224 Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Tue, 21 May 2024 14:25:36 +0200 Subject: [PATCH 3/4] Remove plot's friend --- src/chart/animator/keyframe.cpp | 40 ++++----------------------------- src/chart/animator/keyframe.h | 4 ---- src/chart/animator/morph.cpp | 4 ++-- src/chart/generator/plot.cpp | 26 +++++++++++++++++++++ src/chart/generator/plot.h | 9 ++++---- 5 files changed, 37 insertions(+), 46 deletions(-) diff --git a/src/chart/animator/keyframe.cpp b/src/chart/animator/keyframe.cpp index ce1f4b252..2637840f3 100644 --- a/src/chart/animator/keyframe.cpp +++ b/src/chart/animator/keyframe.cpp @@ -52,20 +52,16 @@ void Keyframe::init(const Gen::PlotPtr &plot, void Keyframe::prepareActual() { if (Gen::Plot::dimensionMatch(*source, *target)) { - addMissingMarkers(source, target); - - mergeMarkerCellInfo(source, target); - - prepareActualMarkersInfo(); + if (Gen::Plot::hasMarkerChange(*source, *target)) + copyTarget(); + Gen::Plot::mergeMarkersWithCellInfo(*source, *target); } else { copyTarget(); - target->prependMarkers(*source); source->appendMarkers(*targetCopy); - - prepareActualMarkersInfo(); } + prepareActualMarkersInfo(); actual = std::make_shared(*source); actual->detachOptions(); @@ -84,34 +80,6 @@ void Keyframe::prepareActualMarkersInfo() smi.insert(std::pair{item.first, Gen::Plot::MarkerInfo{}}); } -void Keyframe::addMissingMarkers(const Gen::PlotPtr &source, - const Gen::PlotPtr &target) -{ - auto &&smarkers = source->markers; - auto &&tmarkers = target->markers; - auto &&ssize = smarkers.size(); - auto &&tsize = tmarkers.size(); - for (auto i = ssize; i < tsize; ++i) - smarkers.emplace_back(tmarkers[i]).enabled = false; - - if (tsize < ssize) copyTarget(); - for (auto i = tsize; i < ssize; ++i) - target->markers.emplace_back(smarkers[i]).enabled = false; -} - -void Keyframe::mergeMarkerCellInfo(const Gen::PlotPtr &source, - const Gen::PlotPtr &target) -{ - const auto markers_size = source->markers.size(); - for (std::size_t ix{}; ix < markers_size; ++ix) - if (auto &scell = source->markers[ix].cellInfo, - &tcell = target->markers[ix].cellInfo; - scell && !tcell) - tcell = scell; - else if (!scell && tcell) - scell = tcell; -} - void Keyframe::copyTarget() { if (!targetCopy) { diff --git a/src/chart/animator/keyframe.h b/src/chart/animator/keyframe.h index 248747a85..48c568623 100644 --- a/src/chart/animator/keyframe.h +++ b/src/chart/animator/keyframe.h @@ -34,10 +34,6 @@ class Keyframe : public Planner const Data::DataTable &dataTable); void prepareActual(); void prepareActualMarkersInfo(); - void addMissingMarkers(const Gen::PlotPtr &source, - const Gen::PlotPtr &target); - static void mergeMarkerCellInfo(const Gen::PlotPtr &source, - const Gen::PlotPtr &target); void copyTarget(); }; diff --git a/src/chart/animator/morph.cpp b/src/chart/animator/morph.cpp index b275078ea..40d318eba 100644 --- a/src/chart/animator/morph.cpp +++ b/src/chart/animator/morph.cpp @@ -51,13 +51,13 @@ void AbstractMorph::transform(double factor) transform(*source.getOptions(), *target.getOptions(), - *actual.options, + *actual.getOptions(), factor); for (auto i = 0U; i < source.getMarkers().size(); ++i) { transform(source.getMarkers()[i], target.getMarkers()[i], - actual.markers[i], + actual.getMarkers()[i], factor); } } diff --git a/src/chart/generator/plot.cpp b/src/chart/generator/plot.cpp index 97296cbee..507671043 100644 --- a/src/chart/generator/plot.cpp +++ b/src/chart/generator/plot.cpp @@ -100,5 +100,31 @@ bool Plot::dimensionMatch(const Plot &a, const Plot &b) return a.getOptions()->getChannels().getDimensions() == b.getOptions()->getChannels().getDimensions(); } +bool Plot::hasMarkerChange(const Plot &source, const Plot &target) +{ + return source.markers.size() != target.markers.size() + || source.markersInfo != target.markersInfo; +} +void Plot::mergeMarkersWithCellInfo(Plot &source, Plot &target) +{ + auto &&smarkers = source.markers; + auto &&tmarkers = target.markers; + auto &&ssize = smarkers.size(); + auto &&tsize = tmarkers.size(); + for (auto i = ssize; i < tsize; ++i) + smarkers.emplace_back(tmarkers[i]).enabled = false; + + for (auto i = tsize; i < ssize; ++i) + tmarkers.emplace_back(smarkers[i]).enabled = false; + + const auto markers_size = std::min(ssize, tsize); + for (std::size_t ix{}; ix < markers_size; ++ix) + if (auto &scell = source.markers[ix].cellInfo, + &tcell = target.markers[ix].cellInfo; + scell && !tcell) + tcell = scell; + else if (!scell && tcell) + scell = tcell; +} } diff --git a/src/chart/generator/plot.h b/src/chart/generator/plot.h index 0d190e31a..8f659cdd8 100644 --- a/src/chart/generator/plot.h +++ b/src/chart/generator/plot.h @@ -31,8 +31,6 @@ namespace Gen class Plot { - friend class Anim::Keyframe; - friend class Anim::Morph::AbstractMorph; friend class PlotBuilder; public: @@ -55,8 +53,6 @@ class Plot using MarkerInfo = ::Anim::Interpolated; using MarkersInfo = std::map; - static bool dimensionMatch(const Plot &a, const Plot &b); - Math::FuzzyBool anyAxisSet; Axises axises; Guides guides; @@ -89,6 +85,11 @@ class Plot void detachOptions(); [[nodiscard]] bool isEmpty() const; + static bool dimensionMatch(const Plot &a, const Plot &b); + static bool hasMarkerChange(const Plot &source, + const Plot &target); + static void mergeMarkersWithCellInfo(Plot &source, Plot &target); + private: PlotOptionsPtr options; Styles::Chart style; From 2b41cc4014cfcabc0cb6b3e63251ce28dc737d3b Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Wed, 22 May 2024 07:27:05 +0200 Subject: [PATCH 4/4] review --- src/base/refl/auto_enum.h | 2 ++ src/chart/animator/animation.cpp | 33 ++++++++++++-------- src/chart/animator/animation.h | 11 ++++--- src/chart/animator/animator.cpp | 11 +++---- src/chart/animator/keyframe.cpp | 2 +- src/chart/generator/axis.h | 48 ++++++++++++++--------------- src/chart/generator/plot.cpp | 2 +- src/chart/generator/plot.h | 2 +- src/chart/generator/plotbuilder.cpp | 44 +++++++++++++++++++------- src/chart/generator/plotbuilder.h | 2 ++ 10 files changed, 94 insertions(+), 63 deletions(-) diff --git a/src/base/refl/auto_enum.h b/src/base/refl/auto_enum.h index 1034d2f65..70260218d 100644 --- a/src/base/refl/auto_enum.h +++ b/src/base/refl/auto_enum.h @@ -173,6 +173,8 @@ struct EnumArray : std::array)> { return base_array::at(static_cast(value)); } + + bool operator==(const EnumArray &) const = default; }; template diff --git a/src/chart/animator/animation.cpp b/src/chart/animator/animation.cpp index 696b1bbb0..0e12e9c93 100644 --- a/src/chart/animator/animation.cpp +++ b/src/chart/animator/animation.cpp @@ -9,10 +9,8 @@ namespace Vizzu::Anim { -Animation::Animation(const Data::DataTable &dataTable, - const Gen::PlotPtr &plot) : +Animation::Animation(const Gen::PlotPtr &plot) : ::Anim::Control(static_cast(*this)), - dataTable(dataTable), source(plot), target(plot) { @@ -39,6 +37,7 @@ Animation::Animation(const Data::DataTable &dataTable, } void Animation::addKeyframe(const Gen::PlotPtr &next, + const Data::DataTable &dataTable, const Options::Keyframe &options) { if (isRunning()) @@ -64,9 +63,11 @@ void Animation::addKeyframe(const Gen::PlotPtr &next, { base.drilldownTo(other); }; - intermediate0 = getIntermediate(target, next, drilldown); + intermediate0 = + getIntermediate(target, next, dataTable, drilldown); - intermediate1 = getIntermediate(next, target, drilldown); + intermediate1 = + getIntermediate(next, target, dataTable, drilldown); } else if (strategy == RegroupStrategy::aggregate) { auto &&targetAxisSet = @@ -101,10 +102,14 @@ void Animation::addKeyframe(const Gen::PlotPtr &next, const auto &base = basedOnSource ? target : next; const auto &other = basedOnSource ? next : target; - intermediate0 = - getIntermediate(base, other, getModifier(basedOnSource)); - intermediate1 = - getIntermediate(base, other, getModifier(!basedOnSource)); + intermediate0 = getIntermediate(base, + other, + dataTable, + getModifier(basedOnSource)); + intermediate1 = getIntermediate(base, + other, + dataTable, + getModifier(!basedOnSource)); } auto &&intermediate0Instant = intermediate0 @@ -140,6 +145,7 @@ void Animation::addKeyframe(const Gen::PlotPtr &next, if (intermediate0) { addKeyframe(begin, intermediate0, + dataTable, real_options, intermediate0Instant); begin = intermediate0; @@ -150,6 +156,7 @@ void Animation::addKeyframe(const Gen::PlotPtr &next, if (intermediate1) { addKeyframe(begin, intermediate1, + dataTable, real_options, intermediate1Instant); begin = intermediate1; @@ -157,7 +164,7 @@ void Animation::addKeyframe(const Gen::PlotPtr &next, if (!intermediate1Instant) real_options.all.delay = ::Anim::Duration(0); } - addKeyframe(begin, next, real_options, nextInstant); + addKeyframe(begin, next, dataTable, real_options, nextInstant); target = next; } @@ -165,7 +172,8 @@ void Animation::addKeyframe(const Gen::PlotPtr &next, template Gen::PlotPtr Animation::getIntermediate(const Gen::PlotPtr &base, const Gen::PlotPtr &other, - Modifier &&modifier) const + const Data::DataTable &dataTable, + Modifier &&modifier) { Gen::PlotPtr res; @@ -187,10 +195,11 @@ Gen::PlotPtr Animation::getIntermediate(const Gen::PlotPtr &base, void Animation::addKeyframe(const Gen::PlotPtr &source, const Gen::PlotPtr &target, + const Data::DataTable &dataTable, const Options::Keyframe &options, bool isInstant) { - ::Anim::Sequence::addKeyframe(std::make_shared(source, + Sequence::addKeyframe(std::make_shared(source, target, dataTable, &options, diff --git a/src/chart/animator/animation.h b/src/chart/animator/animation.h index 81b06d843..4c51e807d 100644 --- a/src/chart/animator/animation.h +++ b/src/chart/animator/animation.h @@ -18,28 +18,29 @@ class Animation : public ::Anim::Sequence, public ::Anim::Control Util::Event onPlotChanged; - Animation(const Data::DataTable &dataTable, - const Gen::PlotPtr &plot); + explicit Animation(const Gen::PlotPtr &plot = {}); void addKeyframe(const Gen::PlotPtr &next, + const Data::DataTable &dataTable, const Options::Keyframe &options); void animate(const ::Anim::Control::Option &options, OnComplete &&onThisCompletes); private: - const Data::DataTable &dataTable; OnComplete completionCallback; Gen::PlotPtr source; Gen::PlotPtr target; template - Gen::PlotPtr getIntermediate(const Gen::PlotPtr &base, + static Gen::PlotPtr getIntermediate(const Gen::PlotPtr &base, const Gen::PlotPtr &other, - Modifier &&modifier) const; + const Data::DataTable &dataTable, + Modifier &&modifier); void addKeyframe(const Gen::PlotPtr &source, const Gen::PlotPtr &target, + const Data::DataTable &dataTable, const Options::Keyframe &options, bool isInstant); }; diff --git a/src/chart/animator/animator.cpp b/src/chart/animator/animator.cpp index d771c1e00..d497f4930 100644 --- a/src/chart/animator/animator.cpp +++ b/src/chart/animator/animator.cpp @@ -11,10 +11,8 @@ Animator::Animator(const Data::DataTable &dataTable, dataTable(dataTable), onBegin(onBegin), onComplete(onComplete), - actAnimation( - std::make_shared(dataTable, Gen::PlotPtr())), - nextAnimation( - std::make_shared(dataTable, Gen::PlotPtr())) + actAnimation(std::make_shared()), + nextAnimation(std::make_shared()) {} void Animator::addKeyframe(const Gen::PlotPtr &plot, @@ -23,7 +21,7 @@ void Animator::addKeyframe(const Gen::PlotPtr &plot, if (running) throw std::logic_error("animation already in progress"); - nextAnimation->addKeyframe(plot, options); + nextAnimation->addKeyframe(plot, dataTable, options); } void Animator::setAnimation(const Anim::AnimationPtr &animation) @@ -40,8 +38,7 @@ void Animator::animate(const ::Anim::Control::Option &options, onThisCompletes.attach( [this](const Gen::PlotPtr &plot, const bool &) { - nextAnimation = - std::make_shared(dataTable, plot); + nextAnimation = std::make_shared(plot); this->running = false; }); diff --git a/src/chart/animator/keyframe.cpp b/src/chart/animator/keyframe.cpp index 2637840f3..1c4f79c76 100644 --- a/src/chart/animator/keyframe.cpp +++ b/src/chart/animator/keyframe.cpp @@ -54,7 +54,7 @@ void Keyframe::prepareActual() if (Gen::Plot::dimensionMatch(*source, *target)) { if (Gen::Plot::hasMarkerChange(*source, *target)) copyTarget(); - Gen::Plot::mergeMarkersWithCellInfo(*source, *target); + Gen::Plot::mergeMarkersAndCellInfo(*source, *target); } else { copyTarget(); diff --git a/src/chart/generator/axis.h b/src/chart/generator/axis.h index 70b59a16e..93618a11c 100644 --- a/src/chart/generator/axis.h +++ b/src/chart/generator/axis.h @@ -16,30 +16,6 @@ namespace Vizzu::Gen { - -template struct AbstractAxises -{ - Refl::EnumArray axises; - - [[nodiscard]] const Type &at(ChannelId channelType) const - { - return axises.at(channelType); - } - - Type &at(ChannelId channelType) { return axises.at(channelType); } - - [[nodiscard]] const Type &other(ChannelId channelType) const - { - return channelType == ChannelId::x ? axises.at(ChannelId::y) - : channelType == ChannelId::y - ? axises.at(ChannelId::x) - : throw std::logic_error("not an axis channel"); - } - - [[nodiscard]] bool operator==( - const AbstractAxises &other) const = default; -}; - struct CommonAxis { ::Anim::String title; @@ -168,9 +144,31 @@ struct Axis DimensionAxis dimension; }; -struct Axises : AbstractAxises +struct Axises { + Refl::EnumArray axises; + [[nodiscard]] const Axis &at(ChannelId channelType) const + { + return axises.at(channelType); + } + + [[nodiscard]] Axis &at(ChannelId channelType) + { + return axises.at(channelType); + } + + [[nodiscard]] const Axis &other(ChannelId channelType) const + { + return channelType == ChannelId::x ? axises.at(ChannelId::y) + : channelType == ChannelId::y + ? axises.at(ChannelId::x) + : throw std::logic_error("not an axis channel"); + } + [[nodiscard]] Geom::Point origo() const; + + [[nodiscard]] bool operator==( + const Axises &other) const = default; }; } diff --git a/src/chart/generator/plot.cpp b/src/chart/generator/plot.cpp index 507671043..828604ff1 100644 --- a/src/chart/generator/plot.cpp +++ b/src/chart/generator/plot.cpp @@ -105,7 +105,7 @@ bool Plot::hasMarkerChange(const Plot &source, const Plot &target) return source.markers.size() != target.markers.size() || source.markersInfo != target.markersInfo; } -void Plot::mergeMarkersWithCellInfo(Plot &source, Plot &target) +void Plot::mergeMarkersAndCellInfo(Plot &source, Plot &target) { auto &&smarkers = source.markers; auto &&tmarkers = target.markers; diff --git a/src/chart/generator/plot.h b/src/chart/generator/plot.h index 8f659cdd8..3c294ed1f 100644 --- a/src/chart/generator/plot.h +++ b/src/chart/generator/plot.h @@ -88,7 +88,7 @@ class Plot static bool dimensionMatch(const Plot &a, const Plot &b); static bool hasMarkerChange(const Plot &source, const Plot &target); - static void mergeMarkersWithCellInfo(Plot &source, Plot &target); + static void mergeMarkersAndCellInfo(Plot &source, Plot &target); private: PlotOptionsPtr options; diff --git a/src/chart/generator/plotbuilder.cpp b/src/chart/generator/plotbuilder.cpp index 47a56745f..736225089 100644 --- a/src/chart/generator/plotbuilder.cpp +++ b/src/chart/generator/plotbuilder.cpp @@ -15,13 +15,8 @@ PlotBuilder::PlotBuilder(const Data::DataTable &dataTable, dataCube(dataTable, *options), plot(std::make_shared(options, style)) { - for (const Channel &ch : - plot->options->getChannels().getChannels()) - if (ch.isDimension()) - plot->axises.at(ch.type).dimension.trackedValues = - std::make_shared< - std::vector>>( - dataCube.combinedSizeOf(ch.dimensions()).second); + initDimensionTrackers(); + std::size_t mainBucketSize{}; auto &&subBuckets = generateMarkers(mainBucketSize); @@ -43,10 +38,28 @@ PlotBuilder::PlotBuilder(const Data::DataTable &dataTable, addAlignment(subBuckets); } + resetDimensionTrackers(); +} + +void PlotBuilder::initDimensionTrackers() const +{ for (const Channel &ch : - plot->options->getChannels().getChannels()) - if (ch.isDimension()) - plot->axises.at(ch.type).dimension.trackedValues.reset(); + plot->options->getChannels().getChannels()) { + if (!ch.isDimension()) continue; + plot->axises.at(ch.type).dimension.trackedValues = + std::make_shared< + std::vector>>( + dataCube.combinedSizeOf(ch.dimensions()).second); + } +} + +void PlotBuilder::resetDimensionTrackers() const +{ + for (const Channel &ch : + plot->options->getChannels().getChannels()) { + if (!ch.isDimension()) continue; + plot->axises.at(ch.type).dimension.trackedValues.reset(); + } } Buckets PlotBuilder::generateMarkers(std::size_t &mainBucketSize) @@ -131,7 +144,16 @@ PlotBuilder::sortedBuckets(const Buckets &buckets, bool main) const } if (main && plot->getOptions()->sort == Sort::byValue) - std::sort(sorted.begin(), sorted.end()); + std::sort(sorted.begin(), + sorted.end(), + [](const std::pair &lhs, + const std::pair &rhs) + { + if (auto ord = std::weak_order(lhs.first, rhs.first); + !std::is_eq(ord)) + return std::is_lt(ord); + return lhs.second < rhs.second; + }); if (main && plot->getOptions()->reverse) std::reverse(sorted.begin(), sorted.end()); diff --git a/src/chart/generator/plotbuilder.h b/src/chart/generator/plotbuilder.h index b50b553f8..29e92bd8f 100644 --- a/src/chart/generator/plotbuilder.h +++ b/src/chart/generator/plotbuilder.h @@ -22,6 +22,8 @@ class PlotBuilder Data::DataCube dataCube; PlotPtr plot; + void initDimensionTrackers() const; + void resetDimensionTrackers() const; Buckets generateMarkers(std::size_t &mainBucketSize); [[nodiscard]] bool linkMarkers(const Buckets &buckets, bool main) const;