gnuplotの3Dグラフには少し癖がある.ここでは,3Dメッシュグラフを滑らかにする方法を記録する.
splot {数式}
であればisosample
で間隔を調整できるが,数値計算の結果などsplot {datafile}
だとisosample
が効かない.データファイルを間引いて描画すればいいのだが,そうすると曲線の解像度が落ちて美しくない.
そこで,多数の線を描画することにより面を構成する方法を記載する.
なお,めんどくさいので目盛りは省略した.
Microsoft Windows 10 Home (64bit)
Gnuplot Version 5.0 patchlevel 4
とりあえす,まずデータファイルを作成する.まあ,なんでもいいのだがここでは,
$$z=\frac{\sin{\sqrt{x^2+y^2}}}{\sqrt{x^2+y^2}}$$を描画しよう.
matrixデータ構造とは,
z11 z12 z13 z14 ... z21 z22 z23 z24 ... z31 z32 z33 z34 ...
というものである.なお,
<N+1> <y0> <y1> <y2> ... <yN> <x0> <z0,0> <z0,1> <z0,2> ... <z0,N> <x1> <z1,0> <z1,1> <z1,2> ... <z1,N> : : : : ... :
という非一様matrixデータ構造を用いれば,後から目盛り情報を付加する必要がなくて便利である.(ここでは一般的なmatrixデータ構造を用いる.)ただし,この時の描画コマンドはsplot {datafile} nonuniform matrix with lines
となる.
「」に示す, で,データファイル(data_raw.dat)を作成して,
set ticslevel 0 unset xtics unset ytics unset ztics splot "data_raw.dat" matrix with lines
で描画したのが下図である.
これだとメッシュが細かすぎてよくわからない.メッシュ数を減らしたかったのだが,isosample
などが効かずにできなかった.
方法を知っている方はぜひコメントにて教えていただきたい.
章のデータファイルを間引いてメッシュを荒くした.データファイル変換スクリプトのソースコードは「」- , である.
set ticslevel 0 unset xtics unset ytics unset ztics splot "cnv1.dat" matrix with lines
で描画すると下図のようになる.特に最大値付近を見るとよく分かるが,格子点間を直線で描画しているため,なめらかな曲線とはいえない.
メッシュの線を曲線として描画させたのが,下図である.
set ticslevel 0 unset xtics unset ytics unset ztics splot "cnv2.dat" with lines
データ構造は,
<x0> <y0> <z00> <x0> <y1> <z01> <x0> <y2> <z02> : : : <x0> <yN> <z0N> <x1> <y0> <z10> <x1> <y1> <z11> <x1> <y2> <z12> : : : <x1> <yN> <z1N> ... <xN> <y0> <zN0> <xN> <y1> <zN1> <xN> <y2> <zN2> : : : <xN> <yN> <zNN> <x0> <y0> <z00> <x1> <y0> <z10> <x2> <y0> <z20> : : : <xN> <y0> <zN0> <x0> <y1> <z01> <x1> <y1> <z11> <x2> <y1> <z21> : : : <xN> <y1> <zN1> ... <x0> <yN> <z0N> <x1> <yN> <z1N> <x2> <yN> <z2N> : : : <xN> <yN> <zNN>
である.ブロック間に2行空行をあけることと,x,yそれぞれに対して曲線を用意することがポイントである.
データファイル変換スクリプトのソースコードは「」- , である.
#include "MyUtil.h" using namespace std; using namespace MyUtil; using MyUtil::status_t; const string OUTPUT_FILE_NAME = "data_raw.dat"; const double RANGE = 10.0; const double dx = 0.1; const int NX = static_cast<int>(2*RANGE / dx) +1; double fn(double x, double y) { if (x==0.0 && y==0.0) return 1.0; return sin(sqrt(x*x+y*y)) / sqrt(x*x+y*y); } // 配列idxを座標に変換 double idx2x(double idx) { int centerIdx = NX / 2; return (idx - centerIdx) * dx; } int main() { ios::sync_with_stdio(false); vector<vector<double>> z(NX, vector<double>(NX, 0)); for(int i=0; i<NX; ++i) { for(int j=0; j<NX; ++j) { z[i][j] = fn(idx2x(i),idx2x(j)); } } output2dVector2Csv(OUTPUT_FILE_NAME, z, "\t"); return 0; }
#include "MyUtil.h" using namespace std; using namespace MyUtil; using MyUtil::status_t; const string OUTPUT_FILE_NAME = "data_cnv1.dat"; const string INPUT_FILE_NAME = "data_raw.dat"; const int NESH_NUM = 21; int main() { ios::sync_with_stdio(false); status_t status; vector<vector<double>> z; tie(status, z) = inputCsv22dVector<double>(INPUT_FILE_NAME, "\t"); const int sizeX = z.size(); const int sizeY = z[0].size(); const int stepX = (sizeX-1) / (NESH_NUM-1); const int stepY = (sizeY-1) / (NESH_NUM-1); // z を間引いて出力. ostringstream oss; for (int i=0; i<sizeX; i+=stepX) { for (int j=0; j<(sizeY-1); j+=stepY) { oss << z[i][j] << "\t"; } oss << z[i][sizeY-1] << "\n"; } outputFile(OUTPUT_FILE_NAME, oss); return 0; }
#include "MyUtil.h" using namespace std; using namespace MyUtil; using MyUtil::status_t; const string OUTPUT_FILE_NAME = "data_cnv2.dat"; const string INPUT_FILE_NAME = "data_raw.dat"; const int NESH_NUM = 21; int main() { ios::sync_with_stdio(false); status_t status; vector<vector<double>> z; tie(status, z) = inputCsv22dVector<double>(INPUT_FILE_NAME, "\t"); const int sizeX = z.size(); const int sizeY = z[0].size(); const int stepX = (sizeX-1) / (NESH_NUM-1); const int stepY = (sizeY-1) / (NESH_NUM-1); // 目盛り用(今回は不使用) double dx = 0.1; double dy = 0.1; // 縦と横の線を独立して,面ではなく複数の線として出力 outputFile(OUTPUT_FILE_NAME, Matrix2Gnuplot3DGridData(z, dx, dy, stepX, stepY)); return 0; }
#ifndef MYUTIL_H #define MYUTIL_H #define _USE_MATH_DEFINES #include <cmath> #include <iostream> #include <sstream> // ss #include <fstream> // ifstream, ofstream #include <iomanip> // マニピュレータ #include <string> #include <vector> #include <tuple> #include <time.h> namespace MyUtil { using namespace std; using status_t = int; // 成功失敗などの返り値用 const status_t STATUS_SUCCESS = 1; const status_t STATUS_FAILURE = 0; // 汎用Print template <typename T> void p(T a); template <typename T> void pn(T a); // 文字列処理系 vector<string> SplitString(const string& input, const string delimiter); template <typename T> vector<T> CastStringVector(const vector<string>& input); // Util tuple<status_t, vector<string>> inputFile2LineVector(const string& filename); template <typename T> tuple<status_t, vector<vector<T>>> inputCsv22dVector(const string& filename, const string delimiter = ","); inline status_t outputFile(const string& filename, const string& str, int isApp = 0); inline status_t outputFile(const string& filename, const ostringstream& oss, int isApp = 0); template <typename T> status_t output2dVector2Csv(const string& filename, vector<vector<T>>& vv, const string delimiter = ","); template <typename T> string Matrix2Gnuplot3DGridData(vector<vector<T>>& vv, double dx, double dy, int stepX, int stepY, double offsetX=0, double offsetY=0); void _PrintErr(const string& s); // ######################################## // 汎用Print template <typename T> void p(T a) { cout << a << flush; } template <typename T> void pn(T a) { cout << a << endl; } // ######################################## // 文字列処理系 vector<string> SplitString(const string& input, const string delimiter) { vector<string> result; if (input.size() == 0) return result; // delimiter = "" の場合 if (delimiter == "" || delimiter.size() == 0) { for (int i=0;i<input.size();++i) { result.push_back(input.substr(i,1)); } return result; } string::size_type pos = 0; while(pos != string::npos) { string::size_type p = input.find(delimiter, pos); if(p == string::npos) { result.push_back(input.substr(pos)); break; } else { result.push_back(input.substr(pos, p - pos)); } pos = p + delimiter.size(); } return result; } template <typename T> vector<T> CastStringVector(const vector<string>& input) { int size = input.size(); vector<T> result(size); for (int i=0;i<size;++i) { istringstream iss(input[i]); iss >> result[i]; } return result; } // ######################################## // Util tuple<status_t, vector<string>> inputFile2LineVector(const string& filename) { vector<string> result(0); ifstream ifs; ifs.open(filename); string inputString; if (ifs.fail()) { _PrintErr("opne file error at inputFile2LineVector()"); return forward_as_tuple(STATUS_FAILURE, result); } while (getline(ifs, inputString)) { result.push_back(inputString); } ifs.close(); return forward_as_tuple(STATUS_SUCCESS, result); } template <typename T> tuple<status_t, vector<vector<T>>> inputCsv22dVector(const string& filename, const string delimiter) { vector<vector<T>> result(0); status_t status; vector<string> stringLines(0); tie(status, stringLines) = inputFile2LineVector(filename); if (status == STATUS_FAILURE) { return forward_as_tuple(STATUS_FAILURE, result); } for(auto stringLine : stringLines) { result.push_back(CastStringVector<T>(SplitString(stringLine, delimiter))); } return forward_as_tuple(STATUS_SUCCESS, result); } inline status_t outputFile(const string& filename, const string& str, int isApp) { ofstream ofs; if (isApp == 1) { ofs.open(filename, ios::app); } else { ofs.open(filename); } if(! ofs.is_open()) { return STATUS_FAILURE; } ofs << str; ofs.close(); return STATUS_SUCCESS; } inline status_t outputFile(const string& filename, const ostringstream& oss, int isApp) { return outputFile(filename, oss.str(), isApp); } template <typename T> status_t output2dVector2Csv(const string& filename, vector<vector<T>>& vv, const string delimiter) { ostringstream oss; oss.precision(14); int rows = vv.size(); int cols = vv[0].size(); for (int i=0; i<rows; ++i) { for (int j=0; j<cols-1; ++j) { oss << vv[i][j] << delimiter; } oss << vv[i][cols-1] << "\n"; } return outputFile(filename, oss); } template <typename T> string Matrix2Gnuplot3DGridData(vector<vector<T>>& vv, double dx, double dy, int stepX, int stepY, double offsetX, double offsetY) { stringstream ss; int NX = vv.size(); int NY = vv[0].size(); for (int i=0; i<NX; i+=stepX) { for (int j=0; j<NY; ++j) { ss << i*dx+offsetX << "\t" << j*dy+offsetY << "\t" << vv[i][j] << "\n"; } ss << "\n\n"; } for (int i=0; i<NY; i+=stepY) { for (int j=0; j<NX; ++j) { ss << j*dx+offsetX << "\t" << i*dy+offsetY << "\t" << vv[j][i] << "\n"; } ss << "\n\n"; } return ss.str(); } void _PrintErr(const string& s) { p("Error : "); pn(s); pn("Please press Enter key to continue..."); getchar(); } } #endif
名前
Email (※公開されることはありません)
コメント