diff --git a/src/base/refl/auto_enum.h b/src/base/refl/auto_enum.h index 3ab0167b4..0879151f8 100644 --- a/src/base/refl/auto_enum.h +++ b/src/base/refl/auto_enum.h @@ -34,8 +34,29 @@ namespace Detail { template using real_t = std::underlying_type_t; -template From, real_t To> - requires(!std::same_as>) +template +concept UniqueNames = requires { + static_cast(unique_enum_names(E{})); +}; + +template +concept UniqueRange = requires { + static_cast, real_t>>(unique_from_to(E{})); +}; + +template + requires(UniqueRange) +consteval std::pair, real_t> from_to() +{ + constexpr auto res = unique_from_to(E{}); + static_assert(res.first <= res.second, "Invalid range"); + return res; +} + +template From = real_t{}, + real_t To = real_t{}> + requires(!UniqueRange && !std::same_as>) consteval std::pair, real_t> from_to() { if constexpr (std::is_signed_v> @@ -44,39 +65,40 @@ consteval std::pair, real_t> from_to() return from_to(); else if constexpr (!Name::name(To)>().empty()) return from_to(); - else - return {From, To}; + else { + static_assert(From != To, "Not found any enum string"); + return {From, To - 1}; + } } template - requires(std::same_as>) + requires(!UniqueRange && std::same_as>) consteval std::pair, real_t> from_to() { - return {false, false}; + constexpr auto has_false = + !Name::name(false)>().empty(); + constexpr auto has_true = + !Name::name(true)>().empty(); + static_assert(has_false || has_true, "Not found any enum string"); + return {!has_false, has_true}; } template requires(!std::same_as>) consteval real_t count() { - auto [from, to] = from_to(); - return static_cast>(to - from); + auto [from, to] = from_to(); + return static_cast>(to - from + 1); } template requires(std::same_as>) consteval int count() { - return Name::name(false)>().empty() - ? 0 - : 1 + !Name::name(true)>().empty(); + auto [from, to] = from_to(); + return to - from + 1; } -template -concept UniqueNames = requires { - static_cast(unique_enum_names(E{})); -}; - template , F... Ix> consteval auto whole_array(std::integer_sequence = {}) { @@ -87,7 +109,7 @@ consteval auto whole_array(std::integer_sequence = {}) return res; } else { - constexpr auto first = Detail::from_to().first; + constexpr auto first = Detail::from_to().first; std::array(Ix + first)>().size() + ... + (sizeof...(Ix) - 1))> @@ -109,7 +131,7 @@ constexpr std::array enum_name_holder = Detail::whole_array( Detail::count()>{}); template -constexpr std::array enum_name_holder = +constinit const std::array enum_name_holder = Detail::whole_array( std::make_integer_sequence()>{}); @@ -143,7 +165,7 @@ template constexpr std::array enum_names = get_names(); template Type enum_name(E name) { - constexpr auto first = Detail::from_to().first; + constexpr auto first = Detail::from_to().first; constexpr auto n = std::size(enum_names); if (static_cast( static_cast>(name) - first) @@ -159,7 +181,7 @@ Type enum_name(E name) template constexpr E get_enum(const std::string_view &data) { - constexpr auto first = Detail::from_to().first; + constexpr auto first = Detail::from_to().first; Detail::real_t ix{}; for (auto v : enum_names) { if (v == data) break; @@ -204,7 +226,7 @@ struct EnumArray : std::array)> template requires(std::is_enum_v && sizeof...(Args) == Detail::count() - && Detail::from_to().first == 0) + && Detail::from_to().first == 0) struct EnumVariant : std::variant { using base_variant = std::variant; diff --git a/src/chart/animator/planner.cpp b/src/chart/animator/planner.cpp index d3cdf4ea5..7efd5edef 100644 --- a/src/chart/animator/planner.cpp +++ b/src/chart/animator/planner.cpp @@ -425,8 +425,8 @@ bool Planner::needVertical() const != 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) + || source->guides.at(Gen::AxisId::y) + != target->guides.at(Gen::AxisId::y) || (isAnyLegend(Gen::ChannelId::size) && (source->axises.at(Gen::ChannelId::size).common != target->axises.at(Gen::ChannelId::size).common @@ -472,8 +472,8 @@ bool Planner::needHorizontal() const != 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->guides.at(Gen::AxisId::x) + != target->guides.at(Gen::AxisId::x) || source->anyAxisSet != target->anyAxisSet || source->keepAspectRatio != target->keepAspectRatio || (source->markerConnectionOrientation diff --git a/src/chart/generator/axis.h b/src/chart/generator/axis.h index 719334b72..1e07318c0 100644 --- a/src/chart/generator/axis.h +++ b/src/chart/generator/axis.h @@ -154,12 +154,22 @@ struct Axises return axises.at(channelType); } - [[nodiscard]] const Axis &other(ChannelId channelType) const + [[nodiscard]] const Axis &at(AxisId axisType) const { - switch (channelType) { - case ChannelId::x: return axises.at(ChannelId::y); - case ChannelId::y: return axises.at(ChannelId::x); - default: throw std::logic_error("not an axis channel"); + return axises.at(asChannel(axisType)); + } + + [[nodiscard]] Axis &at(AxisId axisType) + { + return axises.at(asChannel(axisType)); + } + + [[nodiscard]] const Axis &other(AxisId axisType) const + { + switch (axisType) { + default: + case AxisId::x: return at(AxisId::y); + case AxisId::y: return at(AxisId::x); } } diff --git a/src/chart/generator/guides.cpp b/src/chart/generator/guides.cpp index c86a52b2a..0d0657c40 100644 --- a/src/chart/generator/guides.cpp +++ b/src/chart/generator/guides.cpp @@ -1,7 +1,5 @@ #include "guides.h" -#include - #include "base/math/interpolation.h" #include "chart/options/align.h" #include "chart/options/channel.h" @@ -92,18 +90,22 @@ Guides::Guides(const Options &options) || (!yIsMeasure && !yOpt.isEmpty()))); } -GuidesByAxis &Guides::at(ChannelId channel) +GuidesByAxis &Guides::at(AxisId channel) { - if (channel == ChannelId::x) return x; - if (channel == ChannelId::y) return y; - throw std::out_of_range("guides index out of range"); + switch (channel) { + default: + case AxisId::x: return x; + case AxisId::y: return y; + } } -const GuidesByAxis &Guides::at(ChannelId channel) const +const GuidesByAxis &Guides::at(AxisId channel) const { - if (channel == ChannelId::x) return x; - if (channel == ChannelId::y) return y; - throw std::out_of_range("guides index out of range"); + switch (channel) { + default: + case AxisId::x: return x; + case AxisId::y: return y; + } } bool Guides::hasAnyGuides() const diff --git a/src/chart/generator/guides.h b/src/chart/generator/guides.h index 6f3738560..4c8fd731b 100644 --- a/src/chart/generator/guides.h +++ b/src/chart/generator/guides.h @@ -32,8 +32,8 @@ struct Guides GuidesByAxis y; explicit Guides(const Options &options); - [[nodiscard]] const GuidesByAxis &at(ChannelId channel) const; - GuidesByAxis &at(ChannelId channel); + [[nodiscard]] const GuidesByAxis &at(AxisId channel) const; + GuidesByAxis &at(AxisId channel); [[nodiscard]] bool hasAnyGuides() const; }; diff --git a/src/chart/generator/plotbuilder.cpp b/src/chart/generator/plotbuilder.cpp index ea70e5ddd..dd5e5c03c 100644 --- a/src/chart/generator/plotbuilder.cpp +++ b/src/chart/generator/plotbuilder.cpp @@ -433,7 +433,7 @@ void PlotBuilder::calcDimensionAxis(ChannelId type) if (scale.isMeasure() || !scale.hasDimension()) return; - auto &&isTypeAxis = isAxis(type); + auto &&isTypeAxis = asAxis(type).has_value(); if (auto merge = scale.labelLevel == 0; isTypeAxis) { for (const auto &marker : plot->markers) { if (!marker.enabled) continue; diff --git a/src/chart/main/style.h b/src/chart/main/style.h index 24ef6cd79..979b514dc 100644 --- a/src/chart/main/style.h +++ b/src/chart/main/style.h @@ -363,9 +363,9 @@ struct PlotParams struct Plot : Padding, Box, PlotParams { - [[nodiscard]] const Axis &getAxis(Gen::ChannelId id) const + [[nodiscard]] const Axis &getAxis(Gen::AxisId id) const { - return id == Gen::ChannelId::x ? xAxis : yAxis; + return id == Gen::AxisId::x ? xAxis : yAxis; } }; diff --git a/src/chart/options/advancedoptions.cpp b/src/chart/options/advancedoptions.cpp deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/chart/options/advancedoptions.h b/src/chart/options/advancedoptions.h deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/chart/options/channel.cpp b/src/chart/options/channel.cpp index 5b839ebec..a04859865 100644 --- a/src/chart/options/channel.cpp +++ b/src/chart/options/channel.cpp @@ -16,9 +16,17 @@ namespace Vizzu::Gen { -bool isAxis(ChannelId type) +std::optional asAxis(ChannelId type) { - return type == ChannelId::x || type == ChannelId::y; + return type == ChannelId::x || type == ChannelId::y + ? std::make_optional(static_cast( + static_cast(type))) + : std::nullopt; +} + +ChannelId asChannel(AxisId type) +{ + return static_cast(static_cast(type)); } Channel Channel::makeChannel(Type id) diff --git a/src/chart/options/channel.h b/src/chart/options/channel.h index 3cf7c59ba..eb8f5e573 100644 --- a/src/chart/options/channel.h +++ b/src/chart/options/channel.h @@ -22,6 +22,28 @@ enum class ChannelId : std::uint8_t { y, noop }; +using ChannelIdType = std::underlying_type_t; + +enum class AxisId : ChannelIdType { + x = static_cast(ChannelId::x), + y = static_cast(ChannelId::y) +}; + +consteval auto unique_from_to(AxisId) +{ + return std::pair{static_cast(AxisId::x), + static_cast(AxisId::y)}; +} + +static_assert(Refl::enum_names.size() == 2); +static_assert(std::ranges::all_of(Refl::enum_names, + [](std::string_view name) + { + return static_cast( + Refl::get_enum(name)) + == static_cast( + Refl::get_enum(name)); + })); class Channel { @@ -68,7 +90,8 @@ class Channel Base::AutoParam step{}; }; -bool isAxis(ChannelId type); +std::optional asAxis(ChannelId type); +ChannelId asChannel(AxisId type); } diff --git a/src/chart/options/options.cpp b/src/chart/options/options.cpp index f5827371a..620f184dc 100644 --- a/src/chart/options/options.cpp +++ b/src/chart/options/options.cpp @@ -66,7 +66,7 @@ const Channel *Options::subAxisOf(ChannelId id) const if (id == ChannelId::size && channels.anyAxisSet()) { return &channels.at(ChannelId::size); } - if (isAxis(id)) { + if (asAxis(id)) { if (channels.at(id).isDimension() && id == mainAxisType()) return &subAxis(); return &channels.at(ChannelId::size); diff --git a/src/chart/options/options.h b/src/chart/options/options.h index 8a4bc42b8..0e7710da8 100644 --- a/src/chart/options/options.h +++ b/src/chart/options/options.h @@ -26,8 +26,6 @@ namespace Vizzu::Gen class Options { - using ChannelIdType = std::underlying_type_t; - public: enum class LegendId : ChannelIdType { color = static_cast(ChannelId::color), diff --git a/src/chart/rendering/drawaxes.cpp b/src/chart/rendering/drawaxes.cpp index 2f3935afb..3cf781e03 100644 --- a/src/chart/rendering/drawaxes.cpp +++ b/src/chart/rendering/drawaxes.cpp @@ -31,8 +31,8 @@ void DrawAxes::drawGeometries() const { interlacing.drawGeometries(); - drawAxis(Gen::ChannelId::x); - drawAxis(Gen::ChannelId::y); + drawAxis(Gen::AxisId::x); + drawAxis(Gen::AxisId::y); DrawGuides{{ctx()}, canvas, painter}.draw(); } @@ -44,13 +44,13 @@ void DrawAxes::drawLabels() const drawDimensionLabels(true); drawDimensionLabels(false); - drawTitle(Gen::ChannelId::x); - drawTitle(Gen::ChannelId::y); + drawTitle(Gen::AxisId::x); + drawTitle(Gen::AxisId::y); } -Geom::Line DrawAxes::getAxis(Gen::ChannelId axisIndex) const +Geom::Line DrawAxes::getAxis(Gen::AxisId axisIndex) const { - auto horizontal = axisIndex == Gen::ChannelId::x; + auto horizontal = axisIndex == Gen::AxisId::x; auto offset = plot->axises.other(axisIndex).measure.origo(); @@ -63,10 +63,10 @@ Geom::Line DrawAxes::getAxis(Gen::ChannelId axisIndex) const return {}; } -void DrawAxes::drawAxis(Gen::ChannelId axisIndex) const +void DrawAxes::drawAxis(Gen::AxisId axisIndex) const { auto eventTarget = - Events::Targets::axis(axisIndex == Gen::ChannelId::x); + Events::Targets::axis(axisIndex == Gen::AxisId::x); auto lineBaseColor = *rootStyle.plot.getAxis(axisIndex).color; @@ -97,7 +97,7 @@ void DrawAxes::drawAxis(Gen::ChannelId axisIndex) const } } -Geom::Point DrawAxes::getTitleBasePos(Gen::ChannelId axisIndex, +Geom::Point DrawAxes::getTitleBasePos(Gen::AxisId axisIndex, ::Anim::InterpolateIndex index) const { typedef Styles::AxisTitle::Position Pos; @@ -125,12 +125,12 @@ Geom::Point DrawAxes::getTitleBasePos(Gen::ChannelId axisIndex, case VPos::begin: break; } - return axisIndex == Gen::ChannelId::x + return axisIndex == Gen::AxisId::x ? Geom::Point{parallel, orthogonal} : Geom::Point{orthogonal, parallel}; } -Geom::Point DrawAxes::getTitleOffset(Gen::ChannelId axisIndex, +Geom::Point DrawAxes::getTitleOffset(Gen::AxisId axisIndex, ::Anim::InterpolateIndex index, bool fades) const { @@ -166,12 +166,12 @@ Geom::Point DrawAxes::getTitleOffset(Gen::ChannelId axisIndex, fades ? calcVSide(titleStyle.vside->get_or_first(index).value) : titleStyle.vside->combine(calcVSide); - return axisIndex == Gen::ChannelId::x + return axisIndex == Gen::AxisId::x ? Geom::Point{parallel, -orthogonal} : Geom::Point{orthogonal, -parallel}; } -void DrawAxes::drawTitle(Gen::ChannelId axisIndex) const +void DrawAxes::drawTitle(Gen::AxisId axisIndex) const { const auto &titleString = plot->axises.at(axisIndex).common.title; @@ -252,7 +252,7 @@ void DrawAxes::drawTitle(Gen::ChannelId axisIndex) const titleStyle, *rootEvents.draw.plot.axis.title, Events::Targets::axisTitle(title.value, - axisIndex == Gen::ChannelId::x), + axisIndex == Gen::AxisId::x), {.colorTransform = Gfx::ColorTransform::Opacity(weight), .flip = upsideDown}); @@ -262,8 +262,7 @@ void DrawAxes::drawTitle(Gen::ChannelId axisIndex) const void DrawAxes::drawDimensionLabels(bool horizontal) const { - auto axisIndex = - horizontal ? Gen::ChannelId::x : Gen::ChannelId::y; + auto axisIndex = horizontal ? Gen::AxisId::x : Gen::AxisId::y; const auto &labelStyle = rootStyle.plot.getAxis(axisIndex).label; @@ -295,8 +294,7 @@ void DrawAxes::drawDimensionLabel(bool horizontal, enabled.labels); if (weight == 0) return; - auto axisIndex = - horizontal ? Gen::ChannelId::x : Gen::ChannelId::y; + auto axisIndex = horizontal ? Gen::AxisId::x : Gen::AxisId::y; const auto &labelStyle = rootStyle.plot.getAxis(axisIndex).label; auto drawLabel = OrientedLabel{{ctx()}}; diff --git a/src/chart/rendering/drawaxes.h b/src/chart/rendering/drawaxes.h index 588fa7135..8b84ffa7c 100644 --- a/src/chart/rendering/drawaxes.h +++ b/src/chart/rendering/drawaxes.h @@ -21,15 +21,14 @@ class DrawAxes : public DrawingContext DrawInterlacing interlacing; private: - [[nodiscard]] Geom::Line getAxis(Gen::ChannelId axisIndex) const; - [[nodiscard]] Geom::Point getTitleBasePos( - Gen::ChannelId axisIndex, + [[nodiscard]] Geom::Line getAxis(Gen::AxisId axisIndex) const; + [[nodiscard]] Geom::Point getTitleBasePos(Gen::AxisId axisIndex, ::Anim::InterpolateIndex index) const; - [[nodiscard]] Geom::Point getTitleOffset(Gen::ChannelId axisIndex, + [[nodiscard]] Geom::Point getTitleOffset(Gen::AxisId axisIndex, ::Anim::InterpolateIndex index, bool fades) const; - void drawAxis(Gen::ChannelId axisIndex) const; - void drawTitle(Gen::ChannelId axisIndex) const; + void drawAxis(Gen::AxisId axisIndex) const; + void drawTitle(Gen::AxisId axisIndex) const; void drawDimensionLabels(bool horizontal) const; void drawDimensionLabel(bool horizontal, const Geom::Point &origo, diff --git a/src/chart/rendering/drawguides.cpp b/src/chart/rendering/drawguides.cpp index bd272755a..a1be45a78 100644 --- a/src/chart/rendering/drawguides.cpp +++ b/src/chart/rendering/drawguides.cpp @@ -23,7 +23,7 @@ void DrawGuides::draw() void DrawGuides::draw(bool horizontal) { - auto axisId = horizontal ? Gen::ChannelId::x : Gen::ChannelId::y; + auto axisId = horizontal ? Gen::AxisId::x : Gen::AxisId::y; const auto &guideStyle = rootStyle.plot.getAxis(axisId).guides; diff --git a/src/chart/rendering/drawinterlacing.cpp b/src/chart/rendering/drawinterlacing.cpp index 99f92fe5b..75e110de4 100644 --- a/src/chart/rendering/drawinterlacing.cpp +++ b/src/chart/rendering/drawinterlacing.cpp @@ -39,8 +39,7 @@ void DrawInterlacing::drawTexts() const void DrawInterlacing::draw(bool horizontal, bool text) const { - auto axisIndex = - horizontal ? Gen::ChannelId::y : Gen::ChannelId::x; + auto axisIndex = horizontal ? Gen::AxisId::y : Gen::AxisId::x; auto interlacingColor = *rootStyle.plot.getAxis(axisIndex).interlacing.color; @@ -111,8 +110,7 @@ void DrawInterlacing::draw( const auto &enabled = horizontal ? plot->guides.y : plot->guides.x; - auto axisIndex = - horizontal ? Gen::ChannelId::y : Gen::ChannelId::x; + auto axisIndex = horizontal ? Gen::AxisId::y : Gen::AxisId::x; const auto &axisStyle = rootStyle.plot.getAxis(axisIndex); @@ -251,8 +249,7 @@ void DrawInterlacing::drawDataLabel( const ::Anim::String &unit, double alpha) const { - auto axisIndex = - horizontal ? Gen::ChannelId::y : Gen::ChannelId::x; + auto axisIndex = horizontal ? Gen::AxisId::y : Gen::AxisId::x; const auto &labelStyle = rootStyle.plot.getAxis(axisIndex).label; auto drawLabel = OrientedLabel{{ctx()}}; @@ -308,8 +305,7 @@ void DrawInterlacing::drawSticks(double tickIntensity, bool horizontal, const Geom::Point &tickPos) const { - auto axisIndex = - horizontal ? Gen::ChannelId::y : Gen::ChannelId::x; + auto axisIndex = horizontal ? Gen::AxisId::y : Gen::AxisId::x; const auto &axisStyle = rootStyle.plot.getAxis(axisIndex); const auto &tickStyle = axisStyle.ticks;