Skip to content

Plotly for C++. A C++ interface to the Plotly.js figure spec, for creating interactive data visualizations.

License

Notifications You must be signed in to change notification settings

jimmyorourke/plotlypp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Pʟᴏᴛʟʏ++

CI MacOS Clang CI Ubuntu GCC CI Ubuntu Clang CI Windows MSVC

Plotly++ is a header-only C++ graphing library for creating interactive plots and charts, with rendering powered by Plotly.js. Plots are generated as HTML, providing interactive visualizations that can be viewed in any modern web browser.

This makes Plotly++ extremely simple and lightweight for C++ applications; no graphics or rendering libraries or frameworks are required! Plotly++'s only additional dependency is a JSON library.

The Plotly++ API provides strong compile-time type safety, enabling active IDE auto-complete and suggestions.

The more than 40 chart types supported by Plotly.js are supported by Plotly++.

Plotly++ is largely auto-generated from the official Plotly.js schema.

Table of Contents

Examples

A comprehensive set of examples demonstrating various chart types can be found in the examples/ directory and the generated interactive plots can be viewed live: View Live Examples

Gallery

Each of the thumbnails below is chart from the the examples/ directory. Click the thumbnail to open the interactive HTML version.

Creating a simple plot

#include <plotlypp/figure.hpp>
#include <plotlypp/traces/scatter.hpp>

void linePlotWithMarkers() {
  using namespace plotlypp;

  std::vector x_data = {1, 2, 3, 4};
  std::vector y_data = {2, 4, 6, 8};

  // Plotly++ uses a fluent API.
  // Trace data (e.g. x, y, z, etc) can be provided as std::vector, std::array,
  // std::span (C++20+), or std::mdspan (C++23+). For custom types like Eigen, see the
  // "Supported Data Types" section of the README, below.
  // Plotly `flaglist` types are specfied with initializer lists. (mode setting is equivalent
  // to "markers+lines" in JavaScript or Python)
  auto scatter_and_lines = Scatter()
                             .x(x_data)
                             .y(y_data)
                             .mode({Scatter::Mode::Lines, Scatter::Mode::Markers})
                             .name("Lines & Markers");

  // Nested types can get verbose, so a lambda-setter API is also available.
  // `title` uses the lambda API, `yaxis` uses the regular setter API, and `xaxis uses both.
  // Also note that adding a layout is optional.
  auto layout = Layout()
                  .title([](auto& t) { t.text("Title of the Graph"); })
                  .xaxis(Layout::Xaxis().title([](auto& t) { t.text("x-axis title"); }))
                  .yaxis(Layout::Yaxis().title(Layout::Yaxis::Title().text("y-axis title")));

  // `xaxis` could alternatively have used nested lambdas.
  layout.xaxis([](auto& x) { x.title([](auto& t) { t.text("New x-axis title"); }); });

  // If you think you really know what you're doing and want to give up type safety, a raw
  // JSON string API is also available.
  layout.yaxis({R"({"title": {"text": "New y-axis title"}})"});

  // The underlying nlohmann::json object can also be accessed and modified directly.
  layout.title().json["font"]["size"] = 24;

  auto figure = Figure()
                  .addTrace(std::move(scatter_and_lines))
                  .setLayout(std::move(layout));

  // Open the plot in the default browser for interactive viewing.
  figure.show();

  // Save the plot to disk for interactive viewing at a later time.
  figure.writeHtml("line_plot_with_markers.html");
}

Building and running the examples

cmake -S . -B build -G <your favourite generator>
cmake --build build
./build/examples/example

When included as part of a larger CMake project, the example target will not be built, unless PLOTLYPP_BUILD_EXAMPLES is set.

Additional Documentation

Since much of Plotly++ is auto-generated from the Plotly.js schema, the official Plotly.js documentation is the best source for additional documentation about trace and chart types and parameters and layout options.

IDE auto-complete and suggestion features are also highly beneficial when working with Plotly++.

Dependencies

The CMakeLists will attempt to use find_package to find nlohmann_json when Plotly++ is built as part of a larger project. When Plotly++ is the main project (eg for just building the examples), CMake will use FetchContent to download nlohmann_json to a project-local directory.

While not directly supported at present, nlohmann JSON could be swapped out for another JSON library will minimal work. Libraries with similar APIs such as Boost JSON could be substituted by updating the alias type in json.hpp. For less similar libraries, and consistent custom data type support, a stronger abstraction would be required.

If regenerating Plotly++ headers, Python3.6+ is required.

Supported Data Types

Plotly++ accepts trace data (e.g., for x, y, z coordinates) in various container types holding arithmetic values or strings:

  • 1D Data: std::vector<T>, std::array<T,N>, and (in C++20+) std::span<T>.
  • 2D Data: std::vector<std::vector<T>>, and (in C++23+) std::mdspan.
  • Custom Types: You can extend Plotly++ to support other array-like types by specializing plotlypp::is_plotly_data_array_extension. Any custom type passed to Plotly++ must additionally be serializable by nlohmann JSON into an appropriate JSON array format. See nlohmann JSON's documentation for details on providing serialization support. The plotlypp/contrib/ directory offers opt-in support providing these specializations for libraries not included as core Plotly++ dependencies. For example, to use Eigen matrices/vectors as trace data, include contrib/eigen_support.hpp and add Eigen as a dependency to your own build target; Plotly++ itself does not depend on Eigen.

Note: examples/3d_charts.cpp includes an example using Eigen (as well as std::mdspan), and as such the examples target depends on Eigen. CMake will use FetchContent to download Eigen to a project-local directory when the examples target is included to be built.

Offline Rendering

The HTML emitted by Plotly++ requires the Plotly.js Javascript library in order to render in a web browser. Plotly++ prefers offline rendering when possible, so the HTML output instructs the web browser to first try to load Plotly.js from a local file in a js subdirectory of the HTML file's directory. Plotly++ emits the required file as js/plotly.min.js when Figure.writeHtml() is called with parameter includeJsResources set to true (the default). If the local Plotly.js file is not found, the Plotly++ HTML will try to acquire and use Plotly.js from the official CDN, requiring an internet connection unless your browser already has it cached.

Usage in Web Applications

In addition to viewing and exporting local HTML plot files, Plotly++ can be used in C++ web server applications to dynamically generate plots for web clients. There are two possible approaches:

1. Generating full HTML

The server can generate a full, self-contained HTML page containing the plot and all necessary JavaScript, which the browser can render directly (the same page produced by Figure.show() and Figure.writeHtml). The Figure.html() method returns the plot as an HTML string, which can be sent in an HTTP response body with a Content-Type: text/html header.

2. Generating Plotly JSON Payload for Client-Side Rendering

Alternatively, the server can act as a JSON API endpoint and generate just the JSON required for the client to render with Plotly.js. The Figure.json() method returns the figure's JSON representation following the Plotly.js figure spec. The figure JSON can be serialized to string and sent in an HTTP response. The client-side page is required to load the Plotly.js library (eg from a CDN), fetch plot data from the server, and use Plotly.newPlot() to render the plot in the browser.

// Server-side C++ pseudo-code for /api/plot-data endpoint:
auto figure = plotlypp::Figure().addTrace(plotlypp::Scatter().y(std::array{2,4,6}));
std::string json_content = figure.json().dump();
// send json_content as HTTP response...
// Client-side JavaScript pseudo-code:
// Assumes Plotly.js is loaded and an element with id="plot-div" exists.
fetch('/api/plot-data')
    .then(response => response.json())
    .then(plotData => {
        Plotly.newPlot('plot-div', plotData.data, plotData.layout);
    });

About

Plotly for C++. A C++ interface to the Plotly.js figure spec, for creating interactive data visualizations.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages