#pragma once #include #include #include #include #include #include #include /** * @namespace gnuplotpp * @brief Pure C++ plotting API that renders through a gnuplot backend. */ namespace gnuplotpp { /** @brief Output file formats supported by the renderer. */ enum class OutputFormat { Pdf, Svg, Eps, Png }; /** @brief Figure-wide palette selection for automatic series colors. */ enum class ColorPalette { Default, Tab10, Viridis, Grayscale }; /** @brief Heatmap colormap palette selection. */ enum class ColorMap { Viridis, Cividis, Turbo, Magma, CoolWarm, Gray }; /** @brief Color normalization mode for color-mapped plots. */ enum class ColorNorm { Linear, Log }; /** @brief Text rendering mode used by terminal setup. */ enum class TextMode { Enhanced, Plain, LaTeX }; /** @brief Coordinate system used by typed annotations. */ enum class CoordSystem { Data, Graph, Screen }; /** @brief Built-in publication presets for size and style defaults. */ enum class Preset { IEEE_SingleColumn, IEEE_DoubleColumn, AIAA_Column, AIAA_Page, IEEE_Tran, Nature_1Col, Elsevier_1Col, Custom }; /** @brief Figure dimensions in inches. */ struct FigureSizeInches { double w = 3.5; double h = 2.5; }; /** @brief Shared style defaults applied at figure scope. */ struct Style { std::string font = "Times-New-Roman"; double font_pt = 9.0; double line_width_pt = 1.0; double point_size = 0.6; bool grid = false; double tick_font_scale = 1.0; double label_font_scale = 1.0; double title_font_scale = 1.0; bool title_bold = false; }; /** @brief Export behavior knobs for format-specific rendering policy. */ struct ExportPolicy { bool drop_line_alpha_for_vector = true; bool warn_line_alpha_on_vector = true; }; /** @brief High-level figure configuration. */ struct FigureSpec { Preset preset = Preset::IEEE_SingleColumn; FigureSizeInches size{}; Style style{}; ColorPalette palette = ColorPalette::Default; TextMode text_mode = TextMode::Enhanced; int rows = 1; int cols = 1; std::vector formats{OutputFormat::Pdf}; std::string title; std::string caption; bool panel_labels = false; bool write_manifest = false; bool share_x = false; bool share_y = false; bool hide_inner_tick_labels = false; bool auto_layout = true; bool interactive_preview = false; std::vector font_fallbacks{"Helvetica", "Arial", "Times"}; ExportPolicy export_policy{}; }; /** @brief Legend placement presets. */ enum class LegendPosition { TopRight, TopLeft, BottomRight, BottomLeft, OutsideRight, OutsideBottom }; /** @brief Legend configuration for one axes. */ struct LegendSpec { bool enabled = true; LegendPosition position = LegendPosition::TopRight; int columns = 1; bool boxed = false; bool opaque = false; bool has_font_pt = false; double font_pt = 8.0; }; /** @brief Typed label annotation. */ struct LabelAnnotation { std::string text; std::string at = "graph 0.05,0.95"; std::string font; bool front = true; }; /** @brief Typed arrow annotation. */ struct ArrowAnnotation { std::string from = "graph 0.1,0.9"; std::string to = "graph 0.2,0.8"; bool heads = true; double line_width_pt = 1.0; std::string color = "#000000"; bool front = true; }; /** @brief Typed rectangle object for highlights/masks. */ struct RectObject { std::string from = "graph 0.1,0.1"; std::string to = "graph 0.2,0.2"; bool has_fill_opacity = false; double fill_opacity = 0.15; std::string fill_color = "#000000"; bool border = false; std::string border_color = "#000000"; bool front = false; }; /** @brief Typed coordinate pair for annotation placement. */ struct Coord2D { CoordSystem system = CoordSystem::Data; double x = 0.0; double y = 0.0; }; /** @brief Typed equation label annotation. */ struct EquationAnnotation { std::string expression; Coord2D at{}; bool boxed = false; bool front = true; }; /** @brief Typed callout with arrow and attached text. */ struct CalloutAnnotation { std::string text; Coord2D from{}; Coord2D to{}; bool heads = true; double line_width_pt = 1.0; std::string color = "#000000"; bool front = true; }; /** @brief Axes typography overrides (optional). */ struct TypographySpec { bool has_tick_font_pt = false; double tick_font_pt = 8.0; bool has_label_font_pt = false; double label_font_pt = 9.0; bool has_title_font_pt = false; double title_font_pt = 9.0; bool has_title_bold = false; bool title_bold = false; }; /** @brief Axes frame/tick style overrides (optional). */ struct AxisFrameSpec { bool has_border_mask = false; int border_mask = 15; bool has_border_line_width_pt = false; double border_line_width_pt = 0.9; bool has_border_color = false; std::string border_color = "#222222"; bool has_ticks_out = false; bool ticks_out = false; bool has_ticks_mirror = false; bool ticks_mirror = false; }; /** @brief Axes-level labels, limits, and grid/log controls. */ struct AxesSpec { std::string title; std::string xlabel; std::string ylabel; std::string y2label; bool grid = false; bool legend = true; LegendSpec legend_spec{}; bool enable_crosshair = false; ColorMap color_map = ColorMap::Viridis; ColorNorm color_norm = ColorNorm::Linear; std::string colorbar_label; bool has_cbrange = false; double cbmin = 0.0; double cbmax = 1.0; bool has_cbtick_step = false; double cbtick_step = 0.1; bool has_xlim = false; double xmin = 0.0; double xmax = 0.0; bool has_ylim = false; double ymin = 0.0; double ymax = 0.0; bool xlog = false; bool ylog = false; bool y2log = false; bool has_y2lim = false; double y2min = 0.0; double y2max = 0.0; bool has_xtick_step = false; double xtick_step = 1.0; bool has_ytick_step = false; double ytick_step = 1.0; bool has_xminor_count = false; int xminor_count = 2; bool has_yminor_count = false; int yminor_count = 2; std::string xformat; std::string yformat; TypographySpec typography{}; AxisFrameSpec frame{}; /** * @deprecated Use `typography.has_tick_font_pt` and `typography.tick_font_pt`. * Legacy fields are normalized in `Axes::set()` for compatibility only. */ bool has_tick_font_pt = false; /** * @deprecated Use `typography.tick_font_pt`. */ double tick_font_pt = 8.0; /** * @deprecated Use `typography.has_label_font_pt` and `typography.label_font_pt`. * Legacy fields are normalized in `Axes::set()` for compatibility only. */ bool has_label_font_pt = false; /** * @deprecated Use `typography.label_font_pt`. */ double label_font_pt = 9.0; /** * @deprecated Use `typography.has_title_font_pt` and `typography.title_font_pt`. * Legacy fields are normalized in `Axes::set()` for compatibility only. */ bool has_title_font_pt = false; /** * @deprecated Use `typography.title_font_pt`. */ double title_font_pt = 9.0; /** * @deprecated Use `typography.has_title_bold` and `typography.title_bold`. * Legacy fields are normalized in `Axes::set()` for compatibility only. */ bool has_title_bold = false; /** * @deprecated Use `typography.title_bold`. */ bool title_bold = false; std::vector labels; std::vector arrows; std::vector rectangles; std::vector equations; std::vector callouts; // Optional raw gnuplot commands for advanced annotations (arrows/labels/etc). std::vector gnuplot_commands; }; /** @brief Supported series drawing types. */ enum class SeriesType { Line, Scatter, ErrorBars, Band, Histogram, Heatmap }; /** @brief Series metadata and optional per-series style override. */ struct SeriesSpec { SeriesType type = SeriesType::Line; std::string label; bool has_line_width = false; double line_width_pt = 1.0; /** @brief Optional explicit line/point color (e.g., "#112233"). */ bool has_color = false; std::string color = "#000000"; /** * @brief Optional opacity in [0, 1]. * @note Backends map this to gnuplot ARGB color strings when possible. */ bool has_opacity = false; double opacity = 1.0; bool use_y2 = false; }; /** @brief In-memory series samples used by backends for emission/rendering. */ struct SeriesData { SeriesSpec spec; std::vector x; std::vector y; std::vector y2; std::vector yerr_low; std::vector yerr_high; std::vector z; }; /** * @brief One subplot in a figure layout. */ class Axes { public: /** * @brief Set axes configuration. * @param spec Axes settings to store. */ void set(AxesSpec spec); /** * @brief Add one x/y series to this axes. * @param spec Series metadata and styling. * @param x X samples. * @param y Y samples; must match x length. * @throws std::invalid_argument if x and y lengths differ. */ void add_series(const SeriesSpec& spec, std::span x, std::span y); /** * @brief Add a confidence band from lower/upper curves. * @param spec Series style metadata. * @param x X samples. * @param y_low Lower bound values. * @param y_high Upper bound values. * @throws std::invalid_argument if lengths differ. */ void add_band(const SeriesSpec& spec, std::span x, std::span y_low, std::span y_high); /** * @brief Add a histogram-style series from bin centers and counts. * @param spec Series metadata. * @param bin_centers Histogram bin centers. * @param counts Histogram values. * @throws std::invalid_argument if lengths differ. */ void add_histogram(const SeriesSpec& spec, std::span bin_centers, std::span counts); /** * @brief Add heatmap samples as x/y/z triplets. * @param spec Series metadata. * @param x X coordinates. * @param y Y coordinates. * @param z Intensity values. * @throws std::invalid_argument if lengths differ. */ void add_heatmap(const SeriesSpec& spec, std::span x, std::span y, std::span z); /** * @brief Add asymmetric y-errorbar series. * @param spec Series metadata. * @param x X samples. * @param y Central values. * @param y_low Absolute lower errors. * @param y_high Absolute upper errors. */ void add_errorbars_asymmetric(const SeriesSpec& spec, std::span x, std::span y, std::span y_low, std::span y_high); /** @return Current axes configuration. */ const AxesSpec& spec() const { return spec_; } /** @return All series added to this axes. */ const std::vector& series() const { return series_; } private: AxesSpec spec_{}; std::vector series_{}; }; class Figure; /** @brief Backend render status and generated output paths. */ enum class RenderStatus { Success, InvalidInput, IoError, ExternalToolMissing, ExternalToolFailure, UnsupportedFormat }; /** @brief Backend render status and generated output paths. */ struct RenderResult { bool ok = true; RenderStatus status = RenderStatus::Success; std::string message; std::filesystem::path script_path; std::vector outputs; }; /** * @brief Renderer backend abstraction. */ class IPlotBackend { public: virtual ~IPlotBackend() = default; /** * @brief Render a figure to output files. * @param fig Figure and series data. * @param out_dir Output directory for artifacts. * @return Render status and paths. */ virtual RenderResult render(const Figure& fig, const std::filesystem::path& out_dir) = 0; }; /** * @brief Validate figure/series invariants before backend rendering. * @param fig Figure to validate. * @return `ok=true` when valid, otherwise InvalidInput with message. */ RenderResult validate_figure_for_render(const Figure& fig); /** * @brief Figure container with a fixed subplot grid and backend delegation. */ class Figure { public: /** * @brief Construct a figure from specification. * @param spec Figure spec including layout. * @throws std::invalid_argument if rows or cols are not positive. */ explicit Figure(FigureSpec spec); /** * @brief Access axes by row/column index. * @param r Zero-based row. * @param c Zero-based column. * @return Mutable axes reference. * @throws std::out_of_range when indices are invalid. */ Axes& axes(int r, int c); /** * @brief Access axes by flattened index. * @param idx Zero-based index in row-major order. * @return Mutable axes reference. * @throws std::out_of_range when index is invalid. */ Axes& axes(int idx); /** @return Figure specification. */ const FigureSpec& spec() const { return spec_; } /** @return All axes in row-major order. */ const std::vector& all_axes() const { return axes_; } /** * @brief Save the figure by invoking the configured backend. * @param out_dir Output directory. * @return Render status and output paths. * @note Returns ok=false when no backend is set. */ RenderResult save(const std::filesystem::path& out_dir) const; /** * @brief Attach a backend used by save(). * @param backend Backend implementation. */ void set_backend(std::shared_ptr backend); private: FigureSpec spec_{}; std::vector axes_{}; std::shared_ptr backend_{}; }; } // namespace gnuplotpp