Skip to content

πŸ“ˆ plotly.cpp: A C++ plotting library for expressive, interactive, real-time and streaming data visualization

License

Notifications You must be signed in to change notification settings

yhisaki/plotly.cpp

Repository files navigation

πŸš€ Plotly.cpp

A C++ plotting library for expressive, interactive, real-time & streaming data visualization

Build Status Documentation codecov Version License C++ Standard Platform

Plotly.cpp Demo

Plotly.cpp brings the power of Plotly.js to C++. Most Plotly.js functions have direct C++ equivalents, making it a familiar plotting library for C++ developers.

Warning

This library is currently under active development

We welcome feedback, bug reports, and contributions to help stabilize the library!

πŸ“‹ Table of Contents

✨ Key Features

  • πŸ”— Plotly.js API Mapping - Translation of most Plotly.js methods

    Plotly.js API Mapping
  • 🎨 Advanced Visualizations - Rich variety of plot types. See gallery for more examples.

    Advanced Visualizations
  • ⚑ Real-Time Updates - Stream data with smooth animations and live updates

    Real-Time Updates
  • πŸ”„ Bidirectional Events - Handle user interactions from C++

    Bidirectional Events

πŸ”„ Plotly.js Compatibility

If you know Plotly.js, you already know Plotly.cpp. The library provides C++ equivalents for Plotly.js functions:

Plotly.js (JavaScript) Plotly.cpp (C++) Purpose
Plotly.newPlot() plotly::Figure::newPlot() Create new plot
Plotly.update() plotly::Figure::update() Update data & layout
Plotly.restyle() plotly::Figure::restyle() Update styling
Plotly.relayout() plotly::Figure::relayout() Update layout only
Plotly.extendTraces() plotly::Figure::extendTraces() Stream data
Plotly.addTraces() plotly::Figure::addTraces() Add new traces
Plotly.deleteTraces() plotly::Figure::deleteTraces() Remove traces
Plotly.downloadImage() plotly::Figure::downloadImage() Save image file

Similar data structures, same parameters, and similar behavior with C++ syntax.

πŸš€ Installation & Quick Start

Prerequisites

  • Ubuntu Linux (tested platform)
  • Chrome/Chromium browser
  • C++17 or higher

Installation

Install from deb package (Recommended)

wget https://github.com/yhisaki/plotly.cpp/releases/download/v0.1.0/libplotly-cpp-0.1.0-Linux.deb
sudo apt install ./libplotly-cpp-0.1.0-Linux.deb

Install from FetchContent

Add to your CMake project using FetchContent:

include(FetchContent)

FetchContent_Declare(
    plotly-cpp
    GIT_REPOSITORY https://github.com/yhisaki/plotly.cpp.git
    GIT_TAG v0.1.0
)

FetchContent_MakeAvailable(plotly-cpp)

Usage

After installation, add the following to your CMakeLists.txt:

find_package(plotly-cpp REQUIRED)

target_link_libraries(your_target plotly-cpp::plotly-cpp)

Dependencies

Plotly.cpp requires the following dependencies:

  • nlohmann/json - JSON serialization/deserialization. You can install it by sudo apt install nlohmann-json3-dev.

πŸ“Š Simple Examples

You can find more details in documentation.

Hello World Example

#include "plotly/plotly.hpp"
#include <vector>

int main() {
    plotly::Figure fig;
    fig.openBrowser();  // Open browser explicitly

    std::vector<double> x = {1, 2, 3, 4, 5};
    std::vector<double> y = {1, 4, 2, 8, 5};

    plotly::Object trace = {
        {"x", x}, {"y", y}, {"type", "scatter"},
        {"mode", "lines+markers"}
    };

    fig.newPlot(plotly::Array{trace});
    fig.waitClose();
    return 0;
}

πŸ“ Complete example

JavaScript equivalent:

Plotly.newPlot("myDiv", [
  {
    x: [1, 2, 3, 4, 5],
    y: [1, 4, 2, 8, 5],
    type: "scatter",
    mode: "lines+markers",
  },
]);

Hello World Example

Real-Time Streaming Data

#include "plotly/plotly.hpp"
#include <chrono>
#include <cmath>
#include <thread>

int main() {
    plotly::Figure fig;
    fig.openBrowser();

    // Create initial empty trace
    plotly::Object trace = {
        {"x", std::vector<double>{}}, {"y", std::vector<double>{}},
        {"type", "scatter"}, {"mode", "lines"}
    };
    std::vector<plotly::Object> data = {trace};
    fig.newPlot(data);

    // Stream sine wave data in real-time
    for (double t = 0; t < 100 && fig.isOpen(); t += 0.1) {
        fig.extendTraces({{"x", {std::vector<double>{t}}},
                          {"y", {std::vector<double>{std::sin(t)}}}}, {0}, 100);
        std::this_thread::sleep_for(std::chrono::milliseconds(50));
    }
    return 0;
}

πŸ“ Complete example

JavaScript equivalent:

function streamData() {
  const newX = [currentTime];
  const newY = [Math.sin(currentTime)];

  Plotly.extendTraces("myDiv", { x: [newX], y: [newY] }, [0], 100);
}

streaming_data

3D Surface Plots

#include "plotly/plotly.hpp"
#include <cmath>

int main() {
    plotly::Figure fig;
    fig.openBrowser();

    // Generate 3D surface data
    int size = 50;
    std::vector<std::vector<double>> z(size, std::vector<double>(size));
    for (int i = 0; i < size; i++) {
        for (int j = 0; j < size; j++) {
            double x = (i - size / 2.0) * 0.2;
            double y = (j - size / 2.0) * 0.2;
            z[i][j] = std::sin(std::sqrt(x * x + y * y));
        }
    }

    plotly::Object trace = {
        {"z", z}, {"type", "surface"}, {"colorscale", "Viridis"}
    };

    fig.newPlot(plotly::Array{trace});
    fig.waitClose();
    return 0;
}

πŸ“ Complete example

JavaScript equivalent:

const z = []; // 2D array of surface data
// Generate 3D surface data...

Plotly.newPlot("myDiv", [
  {
    z: z,
    type: "surface",
    colorscale: "Viridis",
  },
]);

3d_surface

Multi-Trace Styling & Legends

#include "plotly/plotly.hpp"
#include <cmath>
#include <numbers>
#include <vector>

int main() {
    plotly::Figure fig;
    fig.openBrowser();

    int n = 5000;
    std::vector<double> x(n), y(n), z(n), w(n, 2.0);

    for (int i = 0; i < n; ++i) {
        x[i] = i * i;
        y[i] = std::sin(2 * std::numbers::pi * i / 360.0);
        z[i] = std::log(i + 1); // +1 to avoid log(0)
    }

    // Multiple traces with different styles
    plotly::Object trace1 = {{"x", x}, {"y", y}, {"type", "scatter"},
                            {"mode", "lines"}, {"name", "sin(2Ο€i/360)"}};
    plotly::Object trace2 = {{"x", x}, {"y", w}, {"type", "scatter"}, {"mode", "lines"},
                            {"line", {{"color", "red"}, {"dash", "dash"}}},
                            {"name", "constant line (y=2)"}};
    plotly::Object trace3 = {{"x", x}, {"y", z}, {"type", "scatter"},
                            {"mode", "lines"}, {"name", "log(x)"}};

    plotly::Object layout = {
        {"title", {{"text", "Sample figure"}}},
        {"xaxis", {{"range", {0, 1000000}}}}, // xlim equivalent
        {"showlegend", true},
        {"width", 1200}, {"height", 780}
    };

    fig.newPlot({trace1, trace2, trace3}, layout);
    fig.waitClose();
    return 0;
}

πŸ“ Complete example

JavaScript equivalent:

const trace1 = {
  x: x,
  y: y,
  type: "scatter",
  mode: "lines",
  name: "sin(2Ο€i/360)",
};
const trace2 = {
  x: x,
  y: w,
  type: "scatter",
  mode: "lines",
  line: { color: "red", dash: "dash" },
  name: "constant line (y=2)",
};
const trace3 = {
  x: x,
  y: z,
  type: "scatter",
  mode: "lines",
  name: "log(x)",
};

const layout = {
  title: { text: "Sample figure" },
  xaxis: { range: [0, 1000000] },
  showlegend: true,
  width: 1200,
  height: 780,
};

Plotly.newPlot("myDiv", [trace1, trace2, trace3], layout);
Plotly.downloadImage("myDiv", { format: "png", filename: "basic" });

multi-trace-styling

2Γ—2 Subplots

#include "plotly/plotly.hpp"
#include <vector>
#include <cmath>

int main() {
    plotly::Figure fig;

    std::vector<plotly::Object> traces;

    // Create traces for each subplot...

    plotly::Object layout = {
        {"title", {{"text", "2x2 Subplot Grid"}}},
        {"grid", {{"rows", 2}, {"columns", 2}, {"pattern", "independent"}}},
        {"showlegend", false}};

    fig.newPlot(traces, layout);
    fig.waitClose();
    return 0;
}

πŸ“ Complete example

2x2 Subplots

Interactive Event Handling

#include "plotly/plotly.hpp"
#include <vector>

int main() {
    plotly::Figure fig;
    fig.openBrowser();

    // Create plot
    std::vector<double> x = {1, 2, 3, 4, 5};
    std::vector<double> y = {1, 4, 2, 8, 5};
    plotly::Object trace = {{"x", x}, {"y", y}, {"type", "scatter"}, {"mode", "markers"}};
    fig.newPlot(plotly::Array{trace});

    // Register event handlers
    fig.on("plotly_click", [](const plotly::Object &event) {
      // Do something when a point is clicked
    });

    fig.on("plotly_hover", [](const plotly::Object &event) {
        // Do something when a point is hovered
    });

    fig.waitClose();
    return 0;
}

πŸ“ Complete example

event_handling

🎨 Advanced Gallery

More complex examples and use cases are available in the gallery.

gallery

πŸ—οΈ Architecture Overview

You can find more details in Architecture Overview.

Key Components

1. C++ Backend (plotly::Figure)

  • WebSocket Server: Real-time bidirectional communication using libwebsockets
  • HTTP Server: Serves the web frontend assets (HTML, CSS, JavaScript)
  • JSON-RPC Protocol: Structured message passing for plot commands and events
  • Browser Integration: Automatically launches Chrome/Chromium with appropriate configuration

2. Web Frontend (JavaScript)

  • Plotly.js Runtime: The actual plotting engine that renders visualizations
  • WebSocket Client: Connects to C++ backend for command/event exchange
  • JSON-RPC Handler: Maps incoming C++ calls to Plotly.js API functions
  • Event Bridge: Forwards user interactions (clicks, hovers) back to C++

Communication Flow

  1. Startup: The plotly::Figure constructor starts an HTTP server (with an automatically selected port) and a WebSocket server. The browser is opened separately via the openBrowser() method.
  2. Sending Plot Commands: C++ function calls such as fig.newPlot(data) are converted into JSON-RPC messages and return bool indicating success/failure.
  3. Rendering: The frontend receives these messages and invokes the corresponding Plotly.js functions.
  4. Event Handling: If C++ callbacks are registered with fig.on(), the frontend sends event data back to C++ via WebSocket whenever such events occur.

Example Message Flow

fig.newPlot({{"x", {1,2,3}}, {"y", {4,5,6}}, {"type", "scatter"}});

Becomes JSON-RPC message:

{
  "jsonrpc": "2.0",
  "method": "Plotly.newPlot",
  "params": {
    "data": [{ "x": [1, 2, 3], "y": [4, 5, 6], "type": "scatter" }]
  },
  "id": 0
}

Frontend sends back the following JSON-RPC message when the plot is completed:

{
  "jsonrpc": "2.0",
  "result": { "success": true },
  "id": 0
}

πŸ“‹ Complete API Reference

Core Classes

Class Description
plotly::Figure Main plotting interface (replaces HTML div element)
plotly::Object JSON-compatible data container (same as JavaScript objects)

Essential Methods (Direct Plotly.js Equivalents)

C++ Method JavaScript Equivalent Purpose
newPlot(data, layout, config) Plotly.newPlot(div, data, layout, config) Create new plot
update(traceUpdate, layoutUpdate) Plotly.update(div, traceUpdate, layoutUpdate) Update data & layout
relayout(layout) Plotly.relayout(div, layout) Update layout only
restyle(aobj, traces) Plotly.restyle(div, aobj, traces) Update trace styling
extendTraces(update, indices, maxPoints) Plotly.extendTraces(div, update, indices, maxPoints) Stream new data
react(data, layout, config) Plotly.react(div, data, layout, config) Efficient update

Advanced Methods (Direct Plotly.js Equivalents)

C++ Method JavaScript Equivalent Purpose
addTraces(traces, indices) Plotly.addTraces(div, traces, indices) Add new traces
deleteTraces(indices) Plotly.deleteTraces(div, indices) Remove traces
moveTraces(currentIndices, newIndices) Plotly.moveTraces(div, currentIndices, newIndices) Reorder traces
prependTraces(update, indices) Plotly.prependTraces(div, update, indices) Prepend data
addFrames(frames) Plotly.addFrames(div, frames) Add animation frames
animate(frames, opts) Plotly.animate(div, frames, opts) Trigger animations

Export & Utility Methods

C++ Method JavaScript Equivalent Purpose
downloadImage(opts) Plotly.downloadImage(div, opts) Save image file
setDownloadDirectory(path, port) N/A (C++-specific) Set browser download folder
isOpen() N/A (C++-specific) Check connection status
waitClose() N/A (C++-specific) Block until closed

Documentation Reference: Use Plotly.js documentation directly - most examples translate directly to C++.

πŸ—οΈ Development & Contributing

Building from Source

git clone https://github.com/yhisaki/plotly.cpp.git
cd plotly.cpp

# Build everything
make release # or make debug for debug build

# Build with Thread Sanitizer
make tsan

# Build with Address Sanitizer
make asan

Code Quality Tools

# Set up development environment
pip install pre-commit
pre-commit install


# Format code (with clang-format)
make format

# Run clang-tidy
make tidy

# Build and create coverage report
make coverage

πŸ“œ License

This project is licensed under the MIT License - see the LICENSE file for details.

🌟 Star History

Star History Chart

About

πŸ“ˆ plotly.cpp: A C++ plotting library for expressive, interactive, real-time and streaming data visualization

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published