diff --git a/CHANGELOG.md b/CHANGELOG.md index e1f0923a9..f8061d278 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,10 @@ - Add meaning to crossing interlacing. - Do not draw dimension axis labels when the middle of the text is off the plot. +### Added + +- Add spacing property for plot axis style structure. + ## [0.15.0] - 2024-10-28 ### Fixed diff --git a/src/apps/weblib/typeschema-api/styles.yaml b/src/apps/weblib/typeschema-api/styles.yaml index fcb904d59..fe6784f9d 100644 --- a/src/apps/weblib/typeschema-api/styles.yaml +++ b/src/apps/weblib/typeschema-api/styles.yaml @@ -452,6 +452,10 @@ definitions: interlacing: $ref: Interlacing nullable: true + spacing: + type: string + mask: /:number:%/ + nullable: true Plot: $extends: [Padding, Box] diff --git a/src/base/conv/auto_json.h b/src/base/conv/auto_json.h index a50415ea8..764c504bc 100644 --- a/src/base/conv/auto_json.h +++ b/src/base/conv/auto_json.h @@ -75,7 +75,7 @@ struct JSON else if constexpr (std::is_arithmetic_v) { json += toString(val); } - else if constexpr (std::is_enum_v + else if constexpr (Refl::is_enum || std::is_same_v) { json += '\"'; json += toString(val); diff --git a/src/base/conv/parse.h b/src/base/conv/parse.h index f3a8a3dab..243e8e3fb 100644 --- a/src/base/conv/parse.h +++ b/src/base/conv/parse.h @@ -16,7 +16,7 @@ concept Parsable = !std::is_void_v constexpr inline static bool IsParsable = - std::is_enum_v || Parsable + Refl::is_enum || Parsable || (Type::is_optional_v && IsParsable>) || std::is_constructible_v || std::is_same_v || std::is_floating_point_v @@ -28,7 +28,7 @@ template requires IsParsable [[nodiscard]] decltype(auto) parse(const std::string &string) { - if constexpr (std::is_enum_v) + if constexpr (Refl::is_enum) return Refl::get_enum(string); else if constexpr (Parsable) return To::fromString(string); diff --git a/src/base/conv/tostring.h b/src/base/conv/tostring.h index e00207b0a..666a37b78 100644 --- a/src/base/conv/tostring.h +++ b/src/base/conv/tostring.h @@ -19,7 +19,7 @@ concept ToStringMember = template constexpr inline static bool IsStringifiable = - ToStringMember || std::is_enum_v + ToStringMember || Refl::is_enum || (Type::is_optional_v && IsStringifiable>) || std::is_constructible_v @@ -32,7 +32,7 @@ template requires IsStringifiable [[nodiscard]] decltype(auto) toString(const From &value) { - if constexpr (std::is_enum_v) + if constexpr (Refl::is_enum) return Refl::enum_name(value); else if constexpr (Type::is_optional_v) { using T = std::remove_cvref_t; diff --git a/src/base/refl/auto_enum.h b/src/base/refl/auto_enum.h index b9c85b5f0..0b7cfdddb 100644 --- a/src/base/refl/auto_enum.h +++ b/src/base/refl/auto_enum.h @@ -193,12 +193,16 @@ template constexpr E get_enum(const std::string_view &data) return static_cast(ix + first); } +template +concept is_enum = std::is_enum_v && Detail::count() > 0; + template consteval auto enum_values() { constexpr auto first = Detail::from_to().first; constexpr auto n = std::size(enum_names); std::array res{}; for (std::size_t i = 0; i < n; ++i) + // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange) res[i] = static_cast(i + first); return res; } @@ -258,9 +262,8 @@ struct EnumArray : std::array)> bool operator==(const EnumArray &) const = default; }; -template - requires(std::is_enum_v - && sizeof...(Args) == Detail::count() +template + requires(sizeof...(Args) == Detail::count() && Detail::from_to().first == 0) struct EnumVariant : std::variant { diff --git a/src/base/type/physicalvalue.h b/src/base/type/physicalvalue.h index c8c33cc12..b57e10d68 100644 --- a/src/base/type/physicalvalue.h +++ b/src/base/type/physicalvalue.h @@ -1,27 +1,34 @@ #ifndef TYPE_PHYSICALVALUE #define TYPE_PHYSICALVALUE -#include +#include "base/conv/parse.h" +#include "base/conv/tostring.h" +#include "base/refl/auto_enum.h" +#include "base/text/valueunit.h" namespace Type { -enum class SimpleUnit : std::uint8_t { none, relative, absolute }; - -template -class PhysicalValue +template struct PhysicalValue { -public: - Value value; - Unit unit; - - constexpr PhysicalValue() : value{}, unit{} {} - constexpr PhysicalValue(Value value, Unit unit) : - value(value), - unit(unit) - {} + Value value{}; + Unit unit{}; constexpr bool operator==(const PhysicalValue &) const = default; + + template = Value> + [[nodiscard]] static PhysicalValue fromString( + const std::string &str) + { + const Text::ValueUnit vu{str}; + return {vu.getValue(), Refl::get_enum(vu.getUnit())}; + } + + [[nodiscard]] std::string toString() const + { + return Conv::toString(value) + + std::string{Conv::toString(unit)}; + } }; } diff --git a/src/chart/generator/axis.cpp b/src/chart/generator/axis.cpp index 8acf657c7..fad4b2bbe 100644 --- a/src/chart/generator/axis.cpp +++ b/src/chart/generator/axis.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include diff --git a/src/chart/generator/axis.h b/src/chart/generator/axis.h index a5bbdc836..1f80e459d 100644 --- a/src/chart/generator/axis.h +++ b/src/chart/generator/axis.h @@ -29,7 +29,7 @@ struct ChannelStats template [[nodiscard]] const TrackType &at(const T &id) const { - return tracked[-id]; + return tracked[+id]; } void track(ChannelId at, const Data::MarkerId &id) @@ -47,7 +47,7 @@ struct ChannelStats template void setIfRange(Id at, const Math::Range<> &range) { - if (auto *r = std::get_if<0>(&tracked[-at])) *r = range; + if (auto *r = std::get_if<0>(&tracked[+at])) *r = range; } }; diff --git a/src/chart/generator/marker.cpp b/src/chart/generator/marker.cpp index 8a6396b13..87047be9c 100644 --- a/src/chart/generator/marker.cpp +++ b/src/chart/generator/marker.cpp @@ -1,5 +1,6 @@ #include "marker.h" +#include #include #include #include @@ -31,10 +32,10 @@ Marker::Marker(const Options &options, bool needMarkerInfo) : cellInfo(data.cellInfo(index, needMarkerInfo)), mainId(data.getId(mainAxisList, - options.dimLabelIndex(-options.mainAxisType()), + options.dimLabelIndex(+options.mainAxisType()), index)), subId(data.getId(subAxisList, - options.dimLabelIndex(-options.subAxisType()), + options.dimLabelIndex(+options.subAxisType()), index)), sizeId(data.getId( options.getChannels().at(ChannelId::size).dimensions(), @@ -76,7 +77,7 @@ Marker::Marker(const Options &options, if (subAxisList != options.subAxis().dimensions()) subId.label = data.getId(options.subAxis().dimensions(), - options.dimLabelIndex(-options.subAxisType()), + options.dimLabelIndex(+options.subAxisType()), index) .label; @@ -223,14 +224,14 @@ void Marker::fromRectangle(const Geom::Rect &rect) Math::Range<> Marker::getSizeBy(AxisId axisId) const { - return isHorizontal(+axisId) ? toRectangle().hSize() - : toRectangle().vSize(); + return isHorizontal(orientation(axisId)) ? toRectangle().hSize() + : toRectangle().vSize(); } void Marker::setSizeBy(AxisId axisId, const Math::Range<> range) { auto rect = toRectangle(); - if (isHorizontal(+axisId)) + if (isHorizontal(orientation(axisId))) rect.setHSize(range); else rect.setVSize(range); diff --git a/src/chart/generator/plotbuilder.cpp b/src/chart/generator/plotbuilder.cpp index 2331aec10..3b6fb2e34 100644 --- a/src/chart/generator/plotbuilder.cpp +++ b/src/chart/generator/plotbuilder.cpp @@ -17,6 +17,7 @@ #include "base/anim/interpolated.h" #include "base/math/floating.h" #include "base/math/range.h" +#include "base/refl/auto_enum.h" #include "chart/main/style.h" #include "chart/options/align.h" #include "chart/options/channel.h" @@ -206,8 +207,8 @@ bool PlotBuilder::linkMarkers(const Buckets &buckets, std::numeric_limits::lowest()); auto isAggregatable = - !plot->getOptions()->isMeasure(-axisIndex) - || (isMain && plot->getOptions()->isMeasure(-!axisIndex) + !plot->getOptions()->isMeasure(+axisIndex) + || (isMain && plot->getOptions()->isMeasure(+!axisIndex) && plot->getOptions()->geometry.get() == ShapeType::rectangle); @@ -228,7 +229,7 @@ bool PlotBuilder::linkMarkers(const Buckets &buckets, auto &marker = **it.base().base().base(); if (!marker.enabled) continue; o = std::max(o, - marker.size.getCoord(+axisIndex), + marker.size.getCoord(orientation(axisIndex)), Math::Floating::less); } if (o == std::numeric_limits::lowest()) o = 0.0; @@ -274,7 +275,8 @@ bool PlotBuilder::linkMarkers(const Buckets &buckets, : *it.base().base().base(); if (act) - prevPos = act->position.getCoord(+axisIndex) += + prevPos = + act->position.getCoord(orientation(axisIndex)) += isAggregatable ? dimOffset[i] : prevPos; hasConnection |= @@ -350,7 +352,7 @@ void PlotBuilder::calcLegendAndLabel(const Data::DataTable &dataTable) if (scale.title) calcLegend.title = *scale.title; if (auto &&meas = scale.measure()) { - if (plot->getOptions()->isMeasure(-type)) { + if (plot->getOptions()->isMeasure(+type)) { if (isAutoTitle) calcLegend.title = dataCube.getName(*meas); calcLegend.measure = {std::get<0>(stats.at(type)), @@ -364,7 +366,7 @@ void PlotBuilder::calcLegendAndLabel(const Data::DataTable &dataTable) auto merge = type == LegendId::size || (type == LegendId::lightness - && plot->getOptions()->dimLabelIndex(-type) == 0); + && plot->getOptions()->dimLabelIndex(+type) == 0); for (std::uint32_t i{}, count{}; i < indices.size(); ++i) if (const auto &sliceIndex = indices[i]) { auto rangeId = static_cast(i); @@ -412,7 +414,7 @@ void PlotBuilder::calcAxis(const Data::DataTable &dataTable, auto isAutoTitle = scale.title.isAuto(); if (scale.title) axis.title = *scale.title; - if (plot->getOptions()->isMeasure(-type)) { + if (plot->getOptions()->isMeasure(+type)) { const auto &meas = *scale.measure(); if (isAutoTitle) axis.title = dataCube.getName(meas); @@ -431,7 +433,7 @@ void PlotBuilder::calcAxis(const Data::DataTable &dataTable, } else { for (auto merge = - plot->getOptions()->dimLabelIndex(-type) == 0 + plot->getOptions()->dimLabelIndex(+type) == 0 && (type != plot->getOptions()->mainAxisType() || plot->getOptions()->sort != Sort::byValue || scale.dimensions().size() == 1); @@ -504,44 +506,45 @@ void PlotBuilder::addAlignment(const Buckets &subBuckets) const void PlotBuilder::addSeparation(const Buckets &subBuckets, const std::size_t &mainBucketSize) const { - if (plot->getOptions()->isSplit()) { - auto align = plot->getOptions()->align; - - std::vector ranges{mainBucketSize, - Math::Range<>::Raw({}, {})}; - std::vector anyEnabled(mainBucketSize); - - auto &&subAxis = plot->getOptions()->subAxisType(); - for (auto &&bucket : subBuckets) - for (std::size_t i{}, prIx{}; - auto &&[marker, idx] : bucket) { - (i += idx.itemId - std::exchange(prIx, idx.itemId)) %= - ranges.size(); - if (marker.enabled) { - ranges[i].include( - marker.getSizeBy(subAxis).size()); - anyEnabled[i] = true; - } - } + if (!plot->getOptions()->isSplit()) return; - auto max = Math::Range<>::Raw({}, {}); - for (auto i = 0U; 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 (std::size_t i{}, prIx{}; - auto &&[marker, idx] : bucket) { - (i += idx.itemId - std::exchange(prIx, idx.itemId)) %= - ranges.size(); - marker.setSizeBy(subAxis, - Base::Align{align, ranges[i]}.getAligned( - marker.getSizeBy(subAxis))); - } - } + auto align = plot->getOptions()->align; + + std::vector ranges{mainBucketSize, Math::Range<>::Raw({}, {})}; + std::vector anyEnabled(mainBucketSize); + + auto &&subAxis = plot->getOptions()->subAxisType(); + for (auto &&bucket : subBuckets) + for (std::size_t i{}, prIx{}; auto &&[marker, idx] : bucket) { + if (!marker.enabled) continue; + (i += idx.itemId - std::exchange(prIx, idx.itemId)) %= + ranges.size(); + ranges[i].include(marker.getSizeBy(subAxis).size()); + anyEnabled[i] = true; + } + + auto max = Math::Range<>::Raw({}, {}); + for (auto i = 0U; i < ranges.size(); ++i) + if (anyEnabled[i]) max = max + ranges[i]; + + auto splitSpace = + plot->getStyle() + .plot.getAxis(plot->getOptions()->subAxisType()) + .spacing->get(max.getMax(), + plot->getStyle().calculatedSize()); + + for (auto i = 1U; i < ranges.size(); ++i) + ranges[i] = ranges[i] + ranges[i - 1].getMax() + + (anyEnabled[i - 1] ? splitSpace : 0); + + for (auto &&bucket : subBuckets) + for (std::size_t i{}, prIx{}; auto &&[marker, idx] : bucket) { + (i += idx.itemId - std::exchange(prIx, idx.itemId)) %= + ranges.size(); + marker.setSizeBy(subAxis, + Base::Align{align, ranges[i]}.getAligned( + marker.getSizeBy(subAxis))); + } } void PlotBuilder::normalizeSizes() diff --git a/src/chart/main/style.cpp b/src/chart/main/style.cpp index 9769f334d..867a1d59e 100644 --- a/src/chart/main/style.cpp +++ b/src/chart/main/style.cpp @@ -279,7 +279,8 @@ Chart Chart::def() }, .interlacing = { .color = Gfx::Color::Gray(0.97) - } + }, + .spacing = Gfx::Length::Relative(1 / 15.) }, .yAxis = { .color = Gfx::Color::Gray(0.8), @@ -370,7 +371,8 @@ Chart Chart::def() }, .interlacing = { .color = Gfx::Color::Gray(0.97) - } + }, + .spacing = Gfx::Length::Relative(1 / 15.) }, .areaColor = Gfx::Color::Transparent(), .overflow = ::Anim::Interpolated(Overflow::hidden) diff --git a/src/chart/main/style.h b/src/chart/main/style.h index 5af1edcfa..e7fd817ed 100644 --- a/src/chart/main/style.h +++ b/src/chart/main/style.h @@ -245,6 +245,7 @@ struct Axis Tick ticks; Guide guides; Interlacing interlacing; + Param spacing; }; struct MarkerLabelParams diff --git a/src/chart/main/stylesheet.cpp b/src/chart/main/stylesheet.cpp index 52c451ad7..75b5fa851 100644 --- a/src/chart/main/stylesheet.cpp +++ b/src/chart/main/stylesheet.cpp @@ -92,7 +92,7 @@ void Sheet::setPlot() defaultParams.plot.paddingLeft = Gfx::Length::Emphemeral(45.0 / 12.0); } - else if (!options->isMeasure(-options->getVerticalChannel())) { + else if (!options->isMeasure(+options->getVerticalChannel())) { defaultParams.plot.paddingLeft = Gfx::Length::Emphemeral(80.0 / 12.0); } @@ -146,8 +146,8 @@ void Sheet::setMarkers() else { auto sizeMeasure = options->isMeasure(Gen::ChannelId::size); auto mainMeasure = - options->isMeasure(-options->mainAxisType()); - auto subMeasure = options->isMeasure(-options->subAxisType()); + options->isMeasure(+options->mainAxisType()); + auto subMeasure = options->isMeasure(+options->subAxisType()); if (options->geometry == Gen::ShapeType::circle && sizeMeasure && (mainMeasure || subMeasure)) { defaultParams.plot.marker.borderWidth = 1; @@ -155,9 +155,9 @@ void Sheet::setMarkers() } else if (options->geometry == Gen::ShapeType::rectangle) { auto vIsMeasure = - options->isMeasure(-options->getVerticalChannel()); + options->isMeasure(+options->getVerticalChannel()); auto hIsMeasure = - options->isMeasure(-options->getHorizontalChannel()); + options->isMeasure(+options->getHorizontalChannel()); if (auto polar = options->coordSystem.get() == Gen::CoordSystem::polar; polar && options->getVerticalAxis().isEmpty()) diff --git a/src/chart/options/channel.cpp b/src/chart/options/channel.cpp index d8ce2c021..67fa66dda 100644 --- a/src/chart/options/channel.cpp +++ b/src/chart/options/channel.cpp @@ -1,9 +1,6 @@ #include "channel.h" #include -#include -#include -#include #include #include #include diff --git a/src/chart/options/channel.h b/src/chart/options/channel.h index 944f7b8be..f9d6f89dd 100644 --- a/src/chart/options/channel.h +++ b/src/chart/options/channel.h @@ -63,7 +63,7 @@ static_assert(std::ranges::all_of(Refl::enum_names, Refl::get_enum(name)); })); -[[nodiscard]] constexpr Geom::Orientation operator+( +[[nodiscard]] constexpr Geom::Orientation orientation( const AxisId &axis) { return axis == AxisId::x ? Geom::Orientation::horizontal @@ -170,12 +170,12 @@ class Channel Base::AutoParam step{}; }; -[[nodiscard]] constexpr ChannelId operator-(const AxisId &axis) +[[nodiscard]] constexpr ChannelId operator+(const AxisId &axis) { return static_cast(axis); } -[[nodiscard]] constexpr ChannelId operator-(const LegendId &legend) +[[nodiscard]] constexpr ChannelId operator+(const LegendId &legend) { return static_cast(legend); } @@ -183,7 +183,7 @@ class Channel template concept ChannelIdLike = requires(const T &v) { { - -v + +v } -> std::same_as; }; @@ -191,7 +191,7 @@ template [[nodiscard]] constexpr bool operator==(const T &l, const ChannelId &c) { - return -l == c; + return +l == c; } template diff --git a/src/chart/options/channelrange.cpp b/src/chart/options/channelrange.cpp index 6df5c35a7..b88254485 100644 --- a/src/chart/options/channelrange.cpp +++ b/src/chart/options/channelrange.cpp @@ -1,27 +1,10 @@ #include "channelrange.h" -#include - -#include "base/conv/parse.h" -#include "base/conv/tostring.h" #include "base/math/range.h" -#include "base/text/valueunit.h" namespace Vizzu::Gen { -ChannelExtrema::ChannelExtrema(const std::string &str) -{ - const Text::ValueUnit vu(str); - value = vu.getValue(); - unit = Conv::parse(vu.getUnit()); -} - -ChannelExtrema::operator std::string() const -{ - return std::to_string(value) + std::string{Conv::toString(unit)}; -} - Math::Range<> ChannelRange::getRange( const Math::Range<> &original) const { diff --git a/src/chart/options/channelrange.h b/src/chart/options/channelrange.h index d1e94494f..957685da4 100644 --- a/src/chart/options/channelrange.h +++ b/src/chart/options/channelrange.h @@ -23,15 +23,8 @@ consteval auto unique_enum_names(ChannelExtremaType) return ",%,min,max"; } -class ChannelExtrema : - public Type::PhysicalValue -{ -public: - using Base = Type::PhysicalValue; - using Base::PhysicalValue; - explicit ChannelExtrema(const std::string &str); - explicit operator std::string() const; -}; +using ChannelExtrema = + Type::PhysicalValue; using OptionalChannelExtrema = Base::AutoParam; diff --git a/src/chart/options/channels.h b/src/chart/options/channels.h index 9b1c477f4..dd01607d4 100644 --- a/src/chart/options/channels.h +++ b/src/chart/options/channels.h @@ -29,12 +29,12 @@ struct Channels : Refl::EnumArray template [[nodiscard]] const Channel &at(const T &id) const { - return at(-id); + return at(+id); } template [[nodiscard]] Channel &at(const T &id) { - return at(-id); + return at(+id); } void removeSeries(const Data::SeriesIndex &index); diff --git a/src/chart/options/options.cpp b/src/chart/options/options.cpp index 24c61fca2..674825d96 100644 --- a/src/chart/options/options.cpp +++ b/src/chart/options/options.cpp @@ -74,7 +74,7 @@ ChannelId Options::stackChannelType() const if (channels.anyAxisSet()) { switch (geometry.get()) { case ShapeType::area: - case ShapeType::rectangle: return -subAxisType(); + case ShapeType::rectangle: return +subAxisType(); default: case ShapeType::circle: case ShapeType::line: return ChannelId::size; @@ -86,7 +86,7 @@ ChannelId Options::stackChannelType() const std::optional Options::secondaryStackType() const { if (channels.anyAxisSet() && geometry == ShapeType::line) - return -subAxisType(); + return +subAxisType(); return std::nullopt; } @@ -122,7 +122,7 @@ void Options::drilldownTo(const Options &other) { auto &stackChannel = this->stackChannel(); - if (this->split && !isSplit()) this->split = false; + if (this->split && !isSplit()) this->split = {}; for (auto &&dim : other.getChannels().getDimensions()) if (!getChannels().isSeriesUsed(dim)) @@ -316,9 +316,7 @@ void Options::setMeasureRange(Channel &channel, bool positive) if (positive) setRange(channel, 0.0_perc, 110.0_perc); else - setRange(channel, - ChannelExtrema(-10, ChannelExtremaType::relative), - 110.0_perc); + setRange(channel, operator""_perc(-10.0), 110.0_perc); } void Options::setRange(Channel &channel, diff --git a/src/chart/options/options.h b/src/chart/options/options.h index 6d333d495..f826c7273 100644 --- a/src/chart/options/options.h +++ b/src/chart/options/options.h @@ -42,9 +42,9 @@ struct OptionProperties ::Anim::Interpolated geometry{ShapeType::rectangle}; Orientation orientation{OrientationType{}}; Sort sort{Sort::none}; - Math::FuzzyBool reverse{false}; + bool reverse{}; Base::Align::Type align{Base::Align::Type::none}; - Math::FuzzyBool split; + bool split{}; }; class Options : public OptionProperties @@ -121,7 +121,7 @@ class Options : public OptionProperties IdType channel) const { auto &&ch = channels.at(channel); - if (auto dimIndex = dimLabelIndex(-channel)) + if (auto dimIndex = dimLabelIndex(+channel)) return *std::next(ch.set.dimensionIds.begin(), static_cast(*dimIndex)); return ch.labelLevel.getValue(0) == 0 ? ch.measure() diff --git a/src/chart/rendering/drawaxes.cpp b/src/chart/rendering/drawaxes.cpp index b4c56e989..9a147c336 100644 --- a/src/chart/rendering/drawaxes.cpp +++ b/src/chart/rendering/drawaxes.cpp @@ -1,6 +1,9 @@ #include "drawaxes.h" +#include +#include #include +#include #include #include @@ -12,8 +15,12 @@ #include "base/geom/transformedrect.h" #include "base/gfx/colortransform.h" #include "base/gfx/font.h" +#include "base/math/floating.h" #include "base/math/fuzzybool.h" +#include "base/math/interpolation.h" +#include "base/math/range.h" #include "base/math/renard.h" +#include "base/refl/auto_enum.h" #include "base/type/booliter.h" #include "chart/generator/plot.h" // NOLINT(misc-include-cleaner) #include "chart/main/events.h" @@ -145,7 +152,7 @@ void DrawAxes::generateMeasure(Gen::AxisId axisIndex, double stepSize, double weight) { - auto orientation = !+axisIndex; + auto orientation = !Gen::orientation(axisIndex); const auto &meas = getAxis(axisIndex).measure; auto rangeSize = meas.range.size(); auto singleLabelRange = Math::Floating::is_zero(rangeSize); @@ -213,9 +220,9 @@ void DrawAxes::generateMeasure(Gen::AxisId axisIndex, Geom::Line DrawAxes::getAxisLine(Gen::AxisId axisIndex) const { - auto offset = this->origo().getCoord(+!axisIndex); + auto offset = this->origo().getCoord(!orientation(axisIndex)); - auto direction = Geom::Point::Ident(+axisIndex); + auto direction = Geom::Point::Ident(orientation(axisIndex)); auto p0 = direction.flip() * offset; auto p1 = p0 + direction; @@ -266,7 +273,9 @@ Geom::Point DrawAxes::getTitleBasePos(Gen::AxisId axisIndex, default: case Pos::min_edge: break; case Pos::max_edge: orthogonal = 1.0; break; - case Pos::axis: orthogonal = origo().getCoord(+!axisIndex); break; + case Pos::axis: + orthogonal = origo().getCoord(!orientation(axisIndex)); + break; } double parallel{0.0}; @@ -440,7 +449,7 @@ void DrawAxes::drawDimensionLabel(Gen::AxisId axisIndex, double weight) const { if (weight == 0) return; - auto orientation = +axisIndex; + auto orientation = Gen::orientation(axisIndex); const auto &labelStyle = rootStyle.plot.getAxis(axisIndex).label; diff --git a/src/chart/rendering/drawguides.cpp b/src/chart/rendering/drawguides.cpp index f6e41c903..6758b7651 100644 --- a/src/chart/rendering/drawguides.cpp +++ b/src/chart/rendering/drawguides.cpp @@ -1,6 +1,5 @@ #include "drawguides.h" -#include #include #include "base/geom/line.h" @@ -42,8 +41,8 @@ void DrawGuides::drawGuide(Gen::AxisId axisId, { auto eventTarget = Events::Targets::axisGuide(axisId); - auto ident = Geom::Point::Ident(+axisId); - auto normal = Geom::Point::Ident(!+axisId); + auto ident = Geom::Point::Ident(orientation(axisId)); + auto normal = Geom::Point::Ident(!orientation(axisId)); auto relMax = ident * val; parent.canvas.setLineColor(color); diff --git a/src/chart/rendering/drawinterlacing.cpp b/src/chart/rendering/drawinterlacing.cpp index 33fa0ffe6..e8f9312db 100644 --- a/src/chart/rendering/drawinterlacing.cpp +++ b/src/chart/rendering/drawinterlacing.cpp @@ -3,7 +3,8 @@ #include #include #include -#include +#include +#include #include #include "base/anim/interpolated.h" @@ -12,8 +13,8 @@ #include "base/gfx/colortransform.h" #include "base/math/floating.h" #include "base/math/fuzzybool.h" +#include "base/math/interpolation.h" #include "base/math/range.h" -#include "base/math/renard.h" #include "base/text/smartstring.h" #include "base/type/booliter.h" #include "chart/generator/plot.h" // NOLINT(misc-include-cleaner) @@ -51,7 +52,8 @@ void DrawInterlacing::drawGeometries(Gen::AxisId axisIndex) const Math::Floating::less) - clippedBottom; - auto rect = [&, orientation = +axisIndex](const double &from, + auto rect = [&, orientation = orientation(axisIndex)]( + const double &from, const double &to) { return Geom::Rect{ @@ -92,7 +94,7 @@ void DrawInterlacing::drawGeometries(Gen::AxisId axisIndex) const void DrawInterlacing::drawTexts(Gen::AxisId axisIndex) const { const auto &axis = parent.getAxis(axisIndex).measure; - auto orientation = !+axisIndex; + auto orientation = !Gen::orientation(axisIndex); auto origo = parent.origo().getCoord(orientation); const auto &guides = parent.plot->guides.at(axisIndex); const auto &axisStyle = parent.rootStyle.plot.getAxis(axisIndex); @@ -161,7 +163,7 @@ void DrawInterlacing::drawDataLabel( const ::Anim::String &unit, double alpha) const { - auto orientation = !+axisIndex; + auto orientation = !Gen::orientation(axisIndex); const auto &labelStyle = parent.rootStyle.plot.getAxis(axisIndex).label; @@ -232,12 +234,13 @@ void DrawInterlacing::drawSticks(double tickLength, canvas.setLineWidth(*tickStyle.lineWidth); auto tickLine = tickStyle.position->combine( - [tickLine = - parent.coordSys - .convertDirectionAt({tickPos, - tickPos - + Geom::Point::Coord(!+axisIndex, -1.0)}) - .segment(0, tickLength)](const auto &position) + [tickLine = parent.coordSys + .convertDirectionAt({tickPos, + tickPos + + Geom::Point::Coord( + !orientation(axisIndex), + -1.0)}) + .segment(0, tickLength)](const auto &position) { switch (position) { using enum Styles::Tick::Position; diff --git a/src/chart/rendering/drawlegend.cpp b/src/chart/rendering/drawlegend.cpp index f15299b6b..d8dd0039c 100644 --- a/src/chart/rendering/drawlegend.cpp +++ b/src/chart/rendering/drawlegend.cpp @@ -1,7 +1,9 @@ #include "drawlegend.h" +#include #include #include +#include #include #include #include @@ -15,7 +17,9 @@ #include "base/gfx/colortransform.h" #include "base/gfx/draw/roundedrect.h" #include "base/gfx/lineargradient.h" +#include "base/math/floating.h" #include "base/math/fuzzybool.h" +#include "base/math/interpolation.h" #include "base/math/range.h" #include "base/text/smartstring.h" #include "chart/generator/axis.h" diff --git a/src/dataframe/old/datatable.cpp b/src/dataframe/old/datatable.cpp index aa99a6adf..33ab6d87e 100644 --- a/src/dataframe/old/datatable.cpp +++ b/src/dataframe/old/datatable.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include diff --git a/test/e2e/tests/style_tests.json b/test/e2e/tests/style_tests.json index 47039ac70..cb820c8db 100644 --- a/test/e2e/tests/style_tests.json +++ b/test/e2e/tests/style_tests.json @@ -355,6 +355,9 @@ "plot/paddingTop/static_350": { "refs": ["bcfbd92"] }, + "plot/spacing": { + "refs": ["d21c2a7"] + }, "plot/xAxis/color/hexa/animated_yellowCustom_0.25-blueCustom_0.75": { "refs": ["77b26be"] }, diff --git a/test/e2e/tests/style_tests/plot/spacing.mjs b/test/e2e/tests/style_tests/plot/spacing.mjs new file mode 100644 index 000000000..4698c249f --- /dev/null +++ b/test/e2e/tests/style_tests/plot/spacing.mjs @@ -0,0 +1,34 @@ +const testSteps = [ + (chart) => { + const data = { + series: [ + { name: 'Foo', values: ['Alice', 'Bob', 'Bob', 'Ted'] }, + { name: 'Bar', values: ['Happy', 'Happy', 'Sad', 'Sad'] }, + { name: 'Baz', values: [1, 2, 3, 4] } + ] + } + + return chart.animate({ data }) + }, + (chart) => + chart.animate({ + x: { set: ['Bar'] }, + y: { set: ['Baz', 'Foo'], labelLevel: 0 }, + split: true + }), + (chart) => + chart.animate({ + config: { + y: { labelLevel: 1 } + }, + style: { + plot: { + yAxis: { + spacing: '15%' + } + } + } + }) +] + +export default testSteps diff --git a/test/unit/chart/events.cpp b/test/unit/chart/events.cpp index 71fee7b83..abb55ce36 100644 --- a/test/unit/chart/events.cpp +++ b/test/unit/chart/events.cpp @@ -434,7 +434,8 @@ const static auto tests = using Axis = Vizzu::Events::Targets::Axis; for (auto &&[beg, end] = events.equal_range("plot-axis-draw"); const auto &[j, t, l] : values(subrange(beg, end))) - if (!isHorizontal(+static_cast(*t).axis)) + if (!isHorizontal( + orientation(static_cast(*t).axis))) xCenter = std::get(l).line.begin.x; std::set zero_count{}; @@ -608,9 +609,11 @@ const static auto tests = testcase_2}) { chart.getOptions().getChannels().at(y).range.min = - Vizzu::Base::AutoParam{Vizzu::Gen::ChannelExtrema("110%")}; + Vizzu::Base::AutoParam{ + Vizzu::Gen::ChannelExtrema::fromString("110%")}; chart.getOptions().getChannels().at(y).range.max = - Vizzu::Base::AutoParam{Vizzu::Gen::ChannelExtrema("0%")}; + Vizzu::Base::AutoParam{ + Vizzu::Gen::ChannelExtrema::fromString("0%")}; auto &&events = get_events(chart); check->*events.count("plot-axis-draw") == 1u; @@ -627,13 +630,17 @@ const static auto tests = testcase_0}) { chart.getOptions().getChannels().at(y).range.min = - Vizzu::Base::AutoParam{Vizzu::Gen::ChannelExtrema("0.0")}; + Vizzu::Base::AutoParam{ + Vizzu::Gen::ChannelExtrema::fromString("0.0")}; chart.getOptions().getChannels().at(y).range.max = - Vizzu::Base::AutoParam{Vizzu::Gen::ChannelExtrema("1.0")}; + Vizzu::Base::AutoParam{ + Vizzu::Gen::ChannelExtrema::fromString("1.0")}; chart.getOptions().getChannels().at(x).range.min = - Vizzu::Base::AutoParam{Vizzu::Gen::ChannelExtrema("0.0")}; + Vizzu::Base::AutoParam{ + Vizzu::Gen::ChannelExtrema::fromString("0.0")}; chart.getOptions().getChannels().at(x).range.max = - Vizzu::Base::AutoParam{Vizzu::Gen::ChannelExtrema("1.0")}; + Vizzu::Base::AutoParam{ + Vizzu::Gen::ChannelExtrema::fromString("1.0")}; auto &&events = get_events(chart);