最近想要對一些時變的變數進行視覺化,搜尋來搜尋去選擇了使用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");
可用的函式有
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必須為可迭代的物件,且維度一致。
還有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;
}
對於三維圖可以拖動來改變視角,ctrl
+滾輪可以放大縮小,shift
+滾輪可以左右平移。