// // Created by Alan Freitas on 26/08/20. // #ifndef MATPLOTPLUSPLUS_BACKEND_INTERFACE_H #define MATPLOTPLUSPLUS_BACKEND_INTERFACE_H #include #include #include #include namespace matplot { class figure_type; namespace backend { /// Inherit from this class to create a new backend /// - Interactive backends show the plots on a window /// - Non-interactive backends save the plots to a file /// - The only backend for which we currently provide a /// concrete implementation is gnuplot pipes. A gnuplot /// pipe can be an interactive or non-interactive depending /// on the terminal. /// - Unfortunately, backends based on gnuplot terminals /// need to be treated differently because they consume /// commands rather vertices. It might be technically /// possible to come up with a workaround to make gnuplot /// plot each vertex but this is definitely not worth it. /// - The interface for backends that expect lists of /// vertices to draw are somewhat inspired in ImGui backends /// and Matplotlib backends. You can see some examples at: /// https://github.com/matplotlib/matplotlib/blob/master/src/_backend_agg.h /// https://github.com/ocornut/imgui/tree/master/examples /// - If implementing such an interface, the functions /// for gnuplot pipes should just be ignored. /// - These are placeholders with no concrete implementation. /// We still need to define the functions that generate vertices /// in the classes that inherit from axes_object, and these /// functions in the interface are likely to change as we /// need. A good place to start is probably the line object, /// which is fundamental for most plots. class MATPLOT_EXPORTS backend_interface { /// Virtual functions you can override to create any backend public: virtual ~backend_interface() noexcept = default; /// \brief True if backend is in interactive mode /// One backends might support both interactive and /// non-interactive mode. /// For instance, the gnuplot backend can support /// both modes by changing its terminal. /// It is also possible to export the OpenGL state /// as an image with the glReadPixels function. /// You can combine glReadPixels with CImg /// to export the image in a variety of formats. /// To export vector graphics from OpenGL, /// libraries such as http://www.geuz.org/gl2ps/ /// can be used. virtual bool is_interactive(); /// \brief If non-interactive, get the file where we should output /// our data virtual const std::string &output(); /// \brief If non-interactive, get the format in which we should /// output our data virtual const std::string &output_format(); /// \brief If non-interactive, set the file where we should output /// our data This function can use the file extension to /// automatically set the output format virtual bool output(const std::string &filename); /// \brief If non-interactive, set the file and the file format for /// outputting data virtual bool output(const std::string &filename, const std::string &file_format); /// Get the current width /// The user might have changed the image width manually. /// Matplot++ needs to be aware of that. virtual unsigned int width(); /// \brief Get height virtual unsigned int height(); /// \brief Set width /// For when the user programmatically sets the width virtual void width(unsigned int new_width); /// \brief Set height virtual void height(unsigned int new_height); /// \brief Get the current position_x (for interactive backends) /// The user might have changed the image position_x manually. /// Matplot++ needs to be aware of that. virtual unsigned int position_x(); /// \brief Get position_y (for interactive backends) virtual unsigned int position_y(); /// \brief Set position_x (for interactive backends) /// For when the user programmatically sets the position_x virtual void position_x(unsigned int new_position_x); /// \brief Set position_y (for interactive backends) virtual void position_y(unsigned int new_position_y); /// \brief Set window title virtual void window_title(const std::string& title); /// \brief Get window title virtual std::string window_title(); /// \brief Tell the backend we are about to draw a new image /// The backend might reject starting this new image /// For instance, the user already closed the window /// and there's no point in feeding commands to the backend virtual bool new_frame(); /// \brief Tell the backend this new image is over /// The backend is free to plot whatever it's been /// buffering /// \return True if everything is ok virtual bool render_data(); /// \brief Tell the backend to wait for user interaction /// Until then, the backend should block execution if possible /// Figures use this in the show function virtual void show(matplot::figure_type *); /// \brief True if the user requested to close the window /// This function allows the backend to send a signal /// indicating the user has asked to close the window virtual bool should_close(); /// \brief True if the backend supports fonts /// We can avoid some commands if it doesn't virtual bool supports_fonts(); /// Public functions you need to override to create a new /// backend based on vertices, such as OpenGL, Agg, etc... /// These functions are likely to change as we come up /// with concrete implementations based on vertices. /// \see /// https://github.com/matplotlib/matplotlib/blob/master/src/_backend_agg.h /// \see https://github.com/ocornut/imgui/tree/master/examples public: /// \brief Draws background on the image virtual void draw_background(const std::array &color); /// \brief Draws rectangle on the image virtual void draw_rectangle(const double x1, const double x2, const double y1, const double y2, const std::array &color); /// \brief Draw a path on the image /// Many backends will require the path to be floats /// but Matplot++ words with doubles, so it's up to /// the backend to implement this conversion however /// it seems more efficient virtual void draw_path(const std::vector &x, const std::vector &y, const std::array &color); /// \brief Draw markers on the image virtual void draw_markers(const std::vector &x, const std::vector &y, const std::vector &z = {}); /// \brief Draw text on the image virtual void draw_text(const std::vector &x, const std::vector &y, const std::vector &z = {}); /// \brief Draw image matrix on the image virtual void draw_image(const std::vector> &x, const std::vector> &y, const std::vector> &z = {}); /// \brief Draw rectangle on the image virtual void draw_triangle(const std::vector &x, const std::vector &y, const std::vector &z = {}); /// We can certainly include more functions here, such as /// draw_mesh, draw_rectangle, etc... /// However, these functions should have a default implementation /// that would recur to more primitive functions. /// For instance, draw_rectangle should have a default /// implementation to draw a rectangle based on two calls to /// draw_triangle (for filled rectangles) or an implementation based /// on draw_path (for unfilled rectangles). Otherwise, it would be /// very expensive to start a new backend. These function should be /// complementary functions that would be used to improve /// performance on existing backends. /// Public functions you need to override only if your backend /// is based on gnuplot /// If not, just leave it as it is and the default implementations /// should do just fine. public: /// \brief If true, this backend does not work by consuming vertices /// Most functions above will be ignored. /// The figure object will send gnuplot commands to this backend /// instead of vertices. /// The default implementation returns false. virtual bool consumes_gnuplot_commands(); /// \brief Send line and newline to gnu plot pipe and flush /// We can buffer the lines until the end of data is sent virtual void run_command(const std::string &text); /// \brief Include a comment in the gnuplot code /// This is useful when tracing the gnuplot commands /// and when generating a gnuplot file. virtual void include_comment(const std::string &text); }; // class backend_interface } // namespace backend } // namespace matplot #endif // MATPLOTPLUSPLUS_BACKEND_INTERFACE_H