Perlin Noise Marble
noiseモジュールをインストールしてPerlin Noiseを使ってみた。
Python
# conding: utf-8 from PIL import Image import math import noise w = 128 h = 128 pn_freq_u = 10.0 pn_freq_v = 10.0 pn_octave = 3 wv_freq_x = 3.1416 * 2.0 * 10.0 wv_noise_scale = 0.1 img = Image.new('L', (w, h)) p = img.load() for y in range(h): for x in range(w): fx = x / w; fy = y / w; n = noise.pnoise2(fx * pn_freq_u, fy * pn_freq_v, pn_octave) l = (1.0 + math.sin((fx + n * wv_noise_scale) * wv_freq_x)) * 0.5 p[ x, y ] = int(l * 255.0) img.show()
割と好き。
Turing Patterns
Gray-Scottモデルの反応拡散式で模様を自動生成してみました。
C++
pythonでは処理速度が厳しいため反応拡散の計算はC++で書きました。
#define BOOST_PYTHON_STATIC_LIB #include <boost/python.hpp> #include <vector> #include <algorithm> boost::python::list Create(int in_w, int in_h, float in_du, float in_dv, float in_f, float in_k, int in_t) { float* bufferU_0 = new float[in_w * in_h]; float* bufferU_1 = new float[in_w * in_h]; float* bufferV_0 = new float[in_w * in_h]; float* bufferV_1 = new float[in_w * in_h]; float* bufferU[2]; float* bufferV[2]; bufferU[0] = bufferU_0; bufferU[1] = bufferU_1; bufferV[0] = bufferV_0; bufferV[1] = bufferV_1; int current_idx = 0; // Initialize for (int y = 0; y < in_h; ++y) { for (int x = 0; x < in_w; ++x) { int c = y * in_w + x; bufferU[current_idx][c] = 1.0f; bufferV[current_idx][c] = (rand() % 100) < 3 ? 1.0f : 0.0f; } } // Compute for (int t = 0; t < in_t; ++t) { int next_idx = (current_idx + 1) % 2; for (int y = 0; y < in_h; ++y) { for (int x = 0; x < in_w; ++x) { int u = std::max(y - 1, 0) * in_w + x; int d = std::min(y + 1, in_h - 1) * in_w + x; int l = y * in_w + std::max(x - 1, 0); int r = y * in_w + std::min(x + 1, in_w - 1); int c = y * in_w + x; float uu = bufferU[current_idx][u]; float ud = bufferU[current_idx][d]; float ul = bufferU[current_idx][l]; float ur = bufferU[current_idx][r]; float uc = bufferU[current_idx][c]; float vu = bufferV[current_idx][u]; float vd = bufferV[current_idx][d]; float vl = bufferV[current_idx][l]; float vr = bufferV[current_idx][r]; float vc = bufferV[current_idx][c]; float diff_u = in_du * (uu + ud + ur + ul - 4.0f * uc); float diff_v = in_dv * (vu + vd + vr + vl - 4.0f * vc); float reac_x = uc * vc * vc; float reac_u = in_f * (1.0f - uc); float reac_v = (in_f + in_k) * vc; uc += diff_u - reac_x + reac_u; vc += diff_v + reac_x - reac_v; bufferU[next_idx][c] = std::min(1.0f, std::max(0.0f, uc)); bufferV[next_idx][c] = std::min(1.0f, std::max(0.0f, vc)); } } current_idx = next_idx; } // Finalize boost::python::list ret; for (int y = 0; y < in_h; ++y) { for (int x = 0; x < in_w; ++x) { ret.append(bufferV[current_idx][y * in_w + x]); } } delete[] bufferU_0; delete[] bufferU_1; delete[] bufferV_0; delete[] bufferV_1; return ret; } BOOST_PYTHON_MODULE(gs) { boost::python::def("Create", &Create); }
Python
PILを使用してC++の計算結果を画像データに変換しています。
# conding: utf-8 from PIL import Image import gs w = 128 h = 128 Du = 0.1 Dv = 0.05 f = 0.04 k = 0.06 t = 1000 img = Image.new('L', (w, h)) p = img.load() srcImg = gs.Create(w, h, Du, Dv, f, k, t) for y in range(h): for x in range(w): p[ x, y ] = int(srcImg[ y * w + x ] * 255) img.show()
パラメータ設定が難しい。
テキストから Node Graph 作成
Node Graph のデータをテキストにしてみます。
テキストデータ
各行でノードを定義する。 左辺に名前、右辺に定義を書く。 右辺がリストとして解釈された場合、第一要素には型名、第二要素には接続元のリストを記述する。 リスト型で解釈されない場合は値を保持するだけのノードとみなす。
a="月日は" b="百代の過客にして" c="行かふ年も" d="又旅人也" e=["Add",["a","b"]] f=["Add",["c","d"]] g=["Add",["e","f"]] h=0.1 i=1 j=["Add",["h","i"]]
Python
テキストデータを読み込んでノードに変換する。
# conding: utf-8 import json,codecs # 値を保持するだけのノード class Value: def __init__(self, in_value): self.value = in_value def __call__(self): return self.value # 加算ノード class Add: def __init__(self, in_x, in_y): self.x = in_x self.y = in_y def __call__(self): return self.x() + self.y() # ファイル読み込み def load(path): nodes = {} # 右辺評価 def _rhs(exp): x = json.loads(exp) if isinstance(x, list): node, args = x return (globals()[node])(*[nodes[a] for a in args]) else: return Value(x) # 一行ずつ解釈 with codecs.open(path, 'rb', 'utf-8-sig') as f: for s in f.readlines(): k, v = [x.strip() for x in s.split('=',1)] nodes[ k ] = _rhs(v) return nodes # ファイルを読み込んでグラフ作成 graph = load('data.txt') # ノードを評価 print(graph["g"]()) print(graph["j"]())
実行結果
月日は百代の過客にして行かふ年も又旅人也 1.1
パーサー書くの面倒くさい。
Node Graph Architecture
Substance Designer www.youtube.com
巷で流行りの設計。
Python
入出力が定義された各ノードを連結してグラフを作成し、グラフの値がほしいときにノードを辿って必要な解決をおこなう。
# conding: utf-8 # 値を保持するだけのノード class Value: def __init__(self, in_value): self.value = in_value def __call__(self): return self.value # 加算ノード class Add: def __init__(self, in_x, in_y): self.x = in_x self.y = in_y def __call__(self): return self.x() + self.y() # 値 a = Value('月日は') b = Value('百代の過客にして') c = Value('行かふ年も') d = Value('又旅人也') # ノードを繋いでグラフを作成する ab = Add(a, b) cd = Add(c, d) abcd = Add(ab, cd) # ここで初めて演算が実行される print(abcd())
実行するとグラフの演算がおこなわれ、結果が出力される。
月日は百代の過客にして行かふ年も又旅人也
PySideでDirectXウインドウを表示してみる
↑PySideで表示したWindowにDirectXの画面を表示しています。
C++
- pyd用のプロジェクトを作成
- DirectXのTutorialをコピペしてInitWindow、WinProcなどの不要な関数を削除
- InitDeviceでウインドウハンドルを受け取るように変更
- boost::pythonを使ってDirectXの処理をpythonに公開する
#define BOOST_PYTHON_STATIC_LIB #include <boost/python.hpp> #include <d3d11_1.h> #include <d3dcompiler.h> #include <directxmath.h> #include <directxcolors.h> HRESULT InitDevice(PyObject* hwnd, int width, int height); void CleanupDevice(); void Render(); /* コード長いので省略 */ BOOST_PYTHON_MODULE(pydx) { boost::python::def("InitDevice", &InitDevice); boost::python::def("CleanupDevice", &CleanupDevice); boost::python::def("Render", &Render); }
Python
PySideを使ってGUIを作成する。paint系の関数をオーバーライドしてDirectXのRenderが呼ばれるようにしてあげる。
# conding: utf-8 import sys from PySide.QtCore import * from PySide.QtGui import * import pydx # dx view class View(QWidget): def __init__(self, parent=None): super().__init__(parent) self.setAttribute(Qt.WA_PaintOnScreen) self.setAttribute(Qt.WA_NoSystemBackground) self.setAttribute(Qt.WA_OpaquePaintEvent) pydx.InitDevice(self.winId(), parent.width(), parent.height()) def __del__(self): pydx.CleanupDevice() def _render(self): pydx.Render() self.update() # override methods def paintEngine(self, *args): return None def paintEvent(self, *args): self._render() def render(self, *args): self._render() def repaint(self, *args): self._render() myapp = QApplication(sys.argv) wnd = QMainWindow() wnd.resize(640, 480) wnd.setCentralWidget(View(wnd)) wnd.show() wnd.setWindowTitle('pydx') sys.exit(myapp.exec_())
注意点
QWidgetのwinIdがPyCObjectを返すのでHWNDに変換してあげる必要があった。
g_hWnd = HWND(PyCapsule_GetPointer(hwnd, NULL));
リサイズ処理してないのでレンダリング解像度は固定。
boost::pythonでC++のコードをpythonから呼んでみる
まずは単純な関数やクラスを自作してpythonから読んでみます。
C++
いつものようにdllプロジェクトを作成してboostとpythonのlibをリンクします。
出力されるファイルの拡張子をdllからpydに変更しておきます。
#define BOOST_PYTHON_STATIC_LIB #include <boost/python.hpp> class Vector3 { public: float x, y, z; Vector3 Add(const Vector3& rhs) { Vector3 ret; ret.x = this->x + rhs.x; ret.y = this->y + rhs.y; ret.z = this->z + rhs.z; return ret; } }; Vector3 Cross(const Vector3& lhs, const Vector3& rhs) { Vector3 ret; ret.x = lhs.y * rhs.z - lhs.z * rhs.y; ret.y = lhs.z * rhs.x - lhs.x * rhs.z; ret.z = lhs.x * rhs.y - lhs.y * rhs.x; return ret; } BOOST_PYTHON_MODULE(hoge) { boost::python::class_<Vector3>("Vector3") .def_readwrite("x", &Vector3::x) .def_readwrite("y", &Vector3::y) .def_readwrite("z", &Vector3::z) .def("Add", &Vector3::Add); boost::python::def("Cross", Cross); }
Python
C++プロジェクトをビルドしてできあがったpydをpythonでimportします。
モジュールの属性リストを表示する
# conding: utf-8 import hoge print(dir(hoge))
boost::pythonで定義したクラスと関数がpythonにも公開されています。
['Cross', 'Vector3', '__doc__', '__file__', '__loader__', '__name__', '__package__']
実際に使ってみる
# conding: utf-8 import hoge a = hoge.Vector3() b = hoge.Vector3() a.x = 1.0 a.y = 0.0 a.z = 0.0 b.x = 0.0 b.y = 1.0 b.z = 0.0 c = a.Add(b) print(c.x, c.y, c.z) c = hoge.Cross(a, b) print(c.x, c.y, c.z)
pythonからVector3クラスの演算を実行することに成功しました。
1.0 1.0 0.0 0.0 0.0 1.0
boost::python便利です。詳しくは公式ドキュメントを読んでください。
てすと
うあああああああああああああんfどふぁ;なw;えf