Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Axis refactor v8 - DimensionAxis Item multiplicator #608

Merged
merged 3 commits into from
Nov 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
- Markers of lightness are only merged when labelLevel == 0.
- When merge happens, the marker shows the middle value of lightness.
- Remove rare fantom empty marker space on scrollable legend.
- Same legend with different measure series are not interpolated.
- Different legend with same dimension series are interpolated.
- Same legend with different series are not interpolated.
- Different legend with same series are interpolated.
- Invisible axis labels are not draw.
- Fix chaotic axis labels on sorted chart with multiple dimension.

Expand Down
2 changes: 1 addition & 1 deletion src/base/anim/interpolated.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ template <typename Type> class Interpolated

template <class T>
[[nodiscard]] std::optional<InterpolateIndex> get_index(
T &&type) const
const T &type) const
{
if (values[0].value == type) return first;
if (has_second && values[1].value == type) return second;
Expand Down
6 changes: 4 additions & 2 deletions src/base/math/interpolation.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,13 @@ template <class T> concept interpolatable = interpolatable_t<T>{}();
struct interpolate_t
{
template <class T>
constexpr T
constexpr auto
operator()(const T &a, const T &b, double factor) const
{
using Math::interpolate;
if constexpr (interpolatable<T>)
if constexpr (std::is_same_v<std::remove_cvref_t<T>, bool>)
return Math::interpolate<double>(a, b, factor);
else if constexpr (interpolatable<T>)
return interpolate(a, b, factor);
else {
return Refl::visit<T>(
Expand Down
1 change: 0 additions & 1 deletion src/base/text/naturalcmp.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#include "naturalcmp.h"

#include <compare>
#include <string>

#include "character.h"

Expand Down
156 changes: 98 additions & 58 deletions src/chart/generator/axis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@

#include <algorithm>
#include <cmath>
#include <cstdint>
#include <functional>
#include <limits>
#include <map>
#include <optional>
#include <set>
#include <string>
#include <string_view>
#include <tuple>
Expand All @@ -19,6 +22,8 @@
#include "chart/options/channel.h"
#include "dataframe/old/types.h"

#include "colorbase.h"

namespace Vizzu::Gen
{

Expand All @@ -37,45 +42,57 @@ void Axises::addLegendInterpolation(double legendFactor,
if (&source == &empty() && &target == &empty()) return;
using Math::Niebloid::interpolate;

if (source.measure.enabled.get() && target.measure.enabled.get()
&& source.measure.series != target.measure.series) {
if (((source.measure.enabled.get()
&& target.measure.enabled.get())
|| (!source.dimension.empty()
&& !target.dimension.empty()))
&& source.seriesName() != target.seriesName()) {
if (!leftLegend[0]) leftLegend[0].emplace(legendType);
if (!leftLegend[1]) leftLegend[1].emplace(legendType);

leftLegend[0]->calc = interpolate(source, empty(), factor);
leftLegend[1]->calc = interpolate(empty(), target, factor);
return;
}

auto &legendObj =
leftLegend[leftLegend[0]
&& leftLegend[0]->type != legendType];
auto second = leftLegend[0] && leftLegend[0]->type != legendType;
auto &legendObj = leftLegend[second];
if (!legendObj) legendObj.emplace(legendType);
legendObj->calc = interpolate(source, target, factor);
++legendObj->interpolated;

if (leftLegend[0] && leftLegend[1]
&& leftLegend[0]->interpolated == leftLegend[1]->interpolated
&& !leftLegend[0]->calc.dimension.empty()
&& !leftLegend[1]->calc.dimension.empty()
&& leftLegend[0]
->calc.dimension.getValues()
.begin()
->first.column
== leftLegend[1]
->calc.dimension.getValues()
.begin()
->first.column) {
for (auto &item : leftLegend[0]->calc.dimension)
item.weight = 1.0;
for (auto &item : leftLegend[1]->calc.dimension)
item.weight = 1.0;
if (!second) legendFactor = 1.0 - legendFactor;

if (auto sameInterpolated =
leftLegend[0] && leftLegend[1]
&& leftLegend[0]->interpolated
== leftLegend[1]->interpolated
&& leftLegend[0]->calc.seriesName()
== leftLegend[1]->calc.seriesName();
sameInterpolated && !leftLegend[0]->calc.dimension.empty()
&& !leftLegend[1]->calc.dimension.empty()) {

leftLegend[1]->calc.dimension =
leftLegend[0]->calc.dimension =
interpolate(leftLegend[0]->calc.dimension,
leftLegend[1]->calc.dimension,
legendFactor);
}
else if (sameInterpolated
&& leftLegend[0]->calc.measure.enabled.factor(true) > 0
&& leftLegend[1]->calc.measure.enabled.factor(true)
> 0) {

leftLegend[0]->calc.measure.enabled =
::Anim::Interpolated{true};
leftLegend[1]->calc.measure.enabled =
::Anim::Interpolated{true};

leftLegend[1]->calc.measure = leftLegend[0]->calc.measure =
interpolate(leftLegend[0]->calc.measure,
leftLegend[1]->calc.measure,
legendFactor);
}
}

Geom::Point Axises::origo() const
Expand Down Expand Up @@ -114,6 +131,7 @@ MeasureAxis interpolate(const MeasureAxis &op0,
{
MeasureAxis res;
res.enabled = interpolate(op0.enabled, op1.enabled, factor);
res.series = op0.series;

if (op0.enabled.get() && op1.enabled.get()) {
constexpr auto MAX = std::numeric_limits<double>::max() / 2;
Expand Down Expand Up @@ -226,6 +244,7 @@ MeasureAxis interpolate(const MeasureAxis &op0,
res.unit = op0.unit;
}
else if (op1.enabled.get()) {
res.series = op1.series;
res.range = op1.range;
res.step = op1.step;
res.unit = op1.unit;
Expand Down Expand Up @@ -292,48 +311,69 @@ DimensionAxis interpolate(const DimensionAxis &op0,
{
DimensionAxis res;

for (const auto &[slice, item] : op0.values)
res.values.emplace(std::piecewise_construct,
std::tuple{slice},
std::forward_as_tuple(item, true, 1 - factor));

for (const auto &[slice, item] : op1.values) {
auto [resIt, end] = res.values.equal_range(slice);

while (resIt != end && resIt->second.end) { ++resIt; }

if (resIt == end) {
res.values.emplace_hint(resIt,
std::piecewise_construct,
std::tuple{slice},
std::forward_as_tuple(item, false, factor));
res.factor = factor;

for (auto first1 = op0.values.begin(),
first2 = op1.values.begin(),
last1 = op0.values.end(),
last2 = op1.values.end();
first1 != last1 || first2 != last2;)
if (first2 == last2
|| (first1 != last1 && first1->first < first2->first)) {
res.values.emplace(std::piecewise_construct,
std::tuple{first1->first},
std::forward_as_tuple(first1->second, true));
++first1;
}
else if (first1 == last1 || first2->first < first1->first) {
res.values.emplace(std::piecewise_construct,
std::tuple{first2->first},
std::forward_as_tuple(first2->second, false));
++first2;
}
else {
resIt->second.end = true;

resIt->second.range =
Math::interpolate(resIt->second.range,
item.range,
factor);

resIt->second.colorBase =
interpolate(resIt->second.colorBase,
item.colorBase,
factor);

resIt->second.label =
interpolate(resIt->second.label, item.label, factor);

resIt->second.position =
interpolate(resIt->second.position,
item.position,
factor);

resIt->second.weight += item.weight * factor;
auto key = first1->first;
auto to1 = op0.values.upper_bound(key);
auto to2 = op1.values.upper_bound(key);

while (first1 != to1 && first2 != to2)
res.values.emplace(key,
interpolate(first1++->second,
first2++->second,
factor));

for (const auto &latest = std::prev(to2)->second;
first1 != to1;
++first1)
res.values
.emplace(key,
interpolate(first1->second, latest, factor))
->second.end = false;

for (const auto &latest = std::prev(to1)->second;
first2 != to2;
++first2)
res.values
.emplace(key,
interpolate(latest, first2->second, factor))
->second.start = false;
}
}

return res;
}

DimensionAxis::Item interpolate(const DimensionAxis::Item &op0,
const DimensionAxis::Item &op1,
double factor)
{
using Math::Niebloid::interpolate;
DimensionAxis::Item res;
res.start = res.end = true;
res.range = interpolate(op0.range, op1.range, factor);
res.colorBase = interpolate(op0.colorBase, op1.colorBase, factor);
res.label = interpolate(op0.label, op1.label, factor);
res.position = interpolate(op0.position, op1.position, factor);
return res;
}

}
35 changes: 25 additions & 10 deletions src/chart/generator/axis.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,15 @@ struct DimensionAxis

class Item
{
explicit Item() = default;

public:
bool start;
bool end;
bool start{};
bool end{};
Math::Range<double> range;
::Anim::Interpolated<std::uint32_t> position;
::Anim::Interpolated<ColorBase> colorBase;
::Anim::Interpolated<bool> label;
double weight;

Item(Math::Range<double> range,
const std::optional<std::uint32_t> &position,
Expand All @@ -97,27 +98,24 @@ struct DimensionAxis
start(true),
end(true),
range(range),
label(setCategoryAsLabel),
weight(1.0)
label(setCategoryAsLabel)
{
if (position) this->position = *position;
if (color) colorBase = *color;
}

Item(const Item &item, bool starter, double factor) :
Item(const Item &item, bool starter) :
start(starter),
end(!starter),
range(item.range),
position(item.position),
colorBase(item.colorBase),
label(item.label),
weight(item.weight * factor)
label(item.label)
{}

bool operator==(const Item &other) const
{
return range == other.range && weight == other.weight
&& position == other.position;
return range == other.range && position == other.position;
}

[[nodiscard]] bool presentAt(
Expand All @@ -126,9 +124,19 @@ struct DimensionAxis
return (index == ::Anim::first && start)
|| (index == ::Anim::second && end);
}

[[nodiscard]] double weight(double atEnd) const
{
return Math::Niebloid::interpolate(start, end, atEnd);
}

friend Item
interpolate(const Item &op0, const Item &op1, double factor);
};
using Values = std::multimap<Data::SliceIndex, Item>;

double factor{};

DimensionAxis() = default;
bool add(const Data::SliceIndex &index,
const Math::Range<double> &range,
Expand Down Expand Up @@ -170,6 +178,13 @@ struct Axis
MeasureAxis measure;
DimensionAxis dimension;

[[nodiscard]] const std::string &seriesName() const
{
if (!dimension.empty())
return dimension.getValues().begin()->first.column;
return measure.series;
}

[[nodiscard]] bool operator==(const Axis &other) const = default;
};

Expand Down
3 changes: 2 additions & 1 deletion src/chart/generator/plotbuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <limits>
#include <map>
#include <memory>
#include <optional>
#include <ranges>
#include <stdexcept>
#include <tuple>
#include <utility>
#include <vector>
Expand All @@ -31,6 +31,7 @@
#include "dataframe/old/types.h"

#include "buckets.h"
#include "colorbase.h"
#include "plot.h"

namespace Vizzu::Gen
Expand Down
2 changes: 1 addition & 1 deletion src/chart/main/stylesheet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ void Sheet::setAfterStyles(Gen::Plot &plot, const Geom::Size &size)
for (const auto &[label, item] :
plot.axises.at(Gen::AxisId::x).dimension.getValues()) {

if (item.weight == 0 || !item.label.get()) continue;
if (!item.label.get()) continue;

auto textBoundary =
Gfx::ICanvas::textBoundary(font, label.value);
Expand Down
2 changes: 0 additions & 2 deletions src/chart/options/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@
#include "base/text/smartstring.h"
#include "chart/options/channel.h"
#include "chart/options/options.h"
#include "dataframe/interface.h"
#include "dataframe/old/datatable.h"

namespace Vizzu::Gen
{
Expand Down
Loading