Hello World / plɹoM ollǝH

Programmers Live in Vain

Perlin Noise Marble

noiseモジュールをインストールしてPerlin Noiseを使ってみた。

f:id:dungeonneko:20150624232131p:plain

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モデルの反応拡散式で模様を自動生成してみました。

f:id:dungeonneko:20150624223537p:plain

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ウインドウを表示してみる

f:id:dungeonneko:20150607133330p:plain ↑PySideで表示したWindowにDirectXの画面を表示しています。

C++
  1. pyd用のプロジェクトを作成
  2. DirectXのTutorialをコピペしてInitWindow、WinProcなどの不要な関数を削除
  3. InitDeviceでウインドウハンドルを受け取るように変更
  4. 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便利です。詳しくは公式ドキュメントを読んでください。

Boost.Python - 1.58.0