C++使用gnuplot-cpp庫繪製影像

凪风sama發表於2024-07-14

最近想要對一些時變的變數進行視覺化,搜尋來搜尋去選擇了使用gnuplot這個工具。

sudo apt-get install gnuplot
sudo apt-get install gnuplot-x11 # 使其支援linux終端

這樣就安裝完gnuplot了。接著可以在命令列中鍵入gnuplot命令開啟gnuplot的互動式環境。由於這裡著目於使用c++去畫圖,因此命令列下的gnuplot用法並不介紹,貼出幾個2教程gnuplot,
gnuplot教程

接著為了可以在c++中使用gnuplot,綜合各方考慮,選擇了使用gnuplot-cpp這個庫。
gnuplot-cpp

相較於大多數庫都是用linux提供的檔案庫去使用字串透過管道傳參,gnuplot-cpp這個庫有著較好的物件導向特性,較好的封裝以及比較簡潔的介面。而且其實現僅有一個標頭檔案(gnuplot_i.hpp),不需要去寫cmake,非常方便。

唯一的缺點就是沒有啥文件。這個庫也是我在csdn上看到的,那篇作者也沒介紹怎麼用。就根據gnuplot的命令列用法來猜測實驗確定了一些介面的用法,記錄下來。

基礎用法

gnuplot-cpp的主要圍繞著Gnuplot類進行操作。其建構函式接受一個字串來指定圖表中影像的型別。常用的影像型別有:

1. lines 即將相鄰的點連線成線段
2. points 僅將每一點使用符號繪出
3. dots 每一點使用細圓點符號繪出
4. linespoints 組合lines和points的效果
5. impulses 每點的位置會額外畫出一條垂直於x軸的直線
6. steps 相鄰的點連線成階梯狀線段(即橫豎各連線一次)
7. boxes 相鄰的點連線成矩形框,可用來畫柱狀圖

初始化一個Gnuplot物件,並設定影像型別:

#include "gnuplot_i.hpp"
Gnuplot gp(lines); // 預設points

實際上gnuplot命令列模式下繪圖的形態是透過with命令來指定的

plot sin(x) with lines
plot sin(x) with points

這裡的建構函式相當於給with恆定一個引數。當然gnuplot-cpp(以下稱gp)提供了可實時修改的介面。

gp.set_style(points); // 設定影像型別
// 可以更靈活的選擇影像型別

後續的繪圖中,可以看到gnuplot中大體的設定分為plot和set兩個部分。一個用於將資料繪製到圖表中,另一個用於設定圖表的屬性。

基礎介面

gp這個庫的幾乎所有介面都是基於

Gnuplot &Gnuplot::cmd(const std::string &cmdstr)

就像名字一樣,cmd即command的,該介面接受一個命令的字串,並將其傳送給gnuplot命令列。主要就是使用cpp的輸入輸出流將命令透過管道傳給gnuplot來執行各種操作。

因此所有封裝好的介面的引數大多是字串命令,然後經過一些標準化處理後使用cmd()介面傳送給gnuplot。

其中gp庫過載了operator<<來方便的將資料傳送給gnuplot。

inline Gnuplot &operator<<(const std::string &cmdstr)
{
    cmd(cmdstr);
    return (*this);
}
gp << "plot sin(x)" << "with lines";
// 相當於命令列的gunplot環境下直接鍵入
// plot sin(x) with lines;

需要注意的是,Gnuplot類會在析構時銷燬管道檔案,也就意味著顯示的影像也會銷燬,因此需要繪圖完使用一些阻塞手段阻塞其析構,如while(true)或者getchar()

繪製給定函式plot_equation

使用plot_equation函式可以繪製給定函式的影像。其接受一個字串作為函式表示式,以及另一個字串作為圖表的名稱。

#include "gnuplot_i.hpp"
Gnuplot gp(lines);
gp.plot_equation("sin(x)", "sin");

image
可用的函式有

sin(x)
cos(x)
log(x) // 以e為底的對數
exp(x) // e的x次方
x**a // x的a次方
a**x // a的x次方
// 等等

繪製二維點plot_xy

使用plot_xy函式可以繪製二維點的影像。其接受一個二維陣列作為資料,以及另一個字串作為圖表的名稱。

 template <typename X, typename Y>
    Gnuplot &plot_xy(const X &x, const Y &y, const std::stringtitle = "");
#include "gnuplot_i.hpp"
#include <vector>
using namespace std;
Gnuplot gp(linespoints); 
vector<int> x{1, 2, 3, 4, 5};
vector<int> y{1, 4, 9, 16, 25};
gp.plot_xy(x, y, "points"); // 注意x/y必須為可迭代的物件,且維度一致。

image

還有plot_x以及plot_xyz介面的用法與plot_xy類似,這裡不再贅述。

設定圖表屬性

gnuplot-cpp提供了一些介面來設定圖表的屬性。

gp.set_title(const std::string &title); // 設定圖表標題
gp.set_xlabel(const std::string &xlabel); // 設定x軸標籤
gp.set_ylabel(const std::string &ylabel); // 設定y軸標籤
gp.set_xrange(const double iFrom, const double iTo) // 設定x軸範圍
gp.set_yrange(const double iFrom, const double iTo) // 設定y軸範圍
gp.set_grid() // 設定網格線
gp.set_title(const std::string &title) // 設定圖表標題
gp.set_yautoscale() // 設定y軸自動縮放
gp.set_xautoscale() // 設定x軸自動縮放
// 還有一些其他屬性設定介面可以自己結合

當設定完畢後,可以呼叫replot()重新plot資料,使其在新設定的屬性下重新繪製。

gp.replot();

最後給出使用gnuplot-cpp繪製三維影像的例子。

#include "gnuplot_i.hpp"
int main()
{
    Gnuplot g10("points");
    g10.set_smooth();
    g10.set_style("linespoints");
    g10.plot_equation3d("sin(x+y)");
    while (true);
    return 0;
}

image

對於三維圖可以拖動來改變視角,ctrl+滾輪可以放大縮小,shift+滾輪可以左右平移。

相關文章