rydotの呟''

プログラミングとかCGとかDTMとか適当にいろいろのことを適度にやる気なく綴るはず。

Mathjaxの式に色を付ける

色を付けるというか、CSSのスタイルをつける。

\classというコマンドがあり、これがCSSのclassに対応しているらしい。

あとは普通にCSSを書けばよいと思う。

  • mathjax:
$$
\begin{array}{ll}
  f(g)=g^2,                   & \class{mathbg-r}{\frac{df}{dg}(g)=2g} \\
  \class{mathbg-y}{g(x)=x+3}, & \class{mathbg-g}{\frac{dg}{dx}(x)=1} \\
\end{array} \\
\begin{align}
  \textstyle \frac{df}{dx}(x)
    & = \textstyle \class{mathbg-r}{\frac{df}{dg}(\class{mathbg-y}{g(x)})} \cdot \class{mathbg-g}{\frac{dg}{dx}(x)} \\
    & = \class{mathbg-r}{(2 \class{mathbg-y}{(x+3)})} \cdot \class{mathbg-g}{(1)} \\
    & = 2x + 6 \\
\end{align}
$$
  span.mathbg-r {
    background-color: #f8d8d0;
  }
  span.mathbg-g {
    background-color: #d8f8d0;
  }
  span.mathbg-y {
    background-color: #f8f0d0;
  }
  • 結果: f:id:rydot:20160711004152p:plain

感想

入れ子でも一応表示してくれるのでうれしいと思う。

wxHaskellのインストールをした。

  • ローカルはwxHaskell
  • webはHaste

という構成をやってみたい。

とりあえずwxHaskellのインストールをした。

WxHaskell - HaskellWiki

環境

使用バージョン

  • wxHaskell 0.92.2.0
  • wxWidgets 3.0.2_4+universal

コマンド

port install wxWidgets-3.0 wxWidgets_select
port select --set wxWidgets wxWidgets-3.0
cabal install wx

けっこうかかる。

cabal直打ちはどうなのかというのはまあ。(

動作確認

公式サイトからコピペで。

-- hello.hs
module Main where

import Graphics.UI.WX

main :: IO ()
main = start gui

gui :: IO ()
gui = do
  frame [text := "Hello World!"]
  return ()
ghc -package wx hello.hs && ./hello

f:id:rydot:20160317005620p:plain

ヾ(๑╹◡╹)ノなんか動いた。

蛇足

  • wx-configが見つからないと言われた。

port selectされてないのでport selectする。

  • バージョンが合わないと言われた。

wxHaskell 0.9系はwxWidgets 3.0系のみ対応らしい。(より新しいバージョンではだめっぽい)

Bezier曲線の算術演算

Bezier曲線の和と積を求める。

Bernstein多項式

Bernstein多項式はBernstein基底関数の線形結合で表現される多項式の形式である。
Bernstein基底関数は  (x + (1-x))^n を2項定理で展開したものである。
n次Bernstein基底関数  b_{i,n}(x) = {{n}\choose{i}}x^i(1-x)^{n-i}
ここで、 {{n}\choose{i}} は2項係数である。
 {{n}\choose{i}} = {}_n C_i = \frac{n!}{(n-i)!i!}

n次Bernstein多項式  B_n(x) = \sum_{i=0}^n \beta_i b_{i,n}(x)

Bernstein多項式 x=0 \beta_0を、 x=1 \beta_nを通過する。

Bezier曲線

Bezier曲線はBernstein多項式の係数 \beta_iをベクトルとしたものである。

n次のBezier曲線  B_n(x) = \sum_{i=0}^n Q_i b_{i,n}(x)
 Q_i をBezier曲線の制御点という。

ここではBernstein多項式とBezier曲線を同じ物とみなす。

次数上げ

n次多項式では  x^{n+1} の係数を0にして n+1次多項式だと言い張ることができる。
しかし、Bernstein多項式では係数操作が必要となる。
n次の係数を Q_i、n+1次の係数を R_iとして、n次からn+1次にするときの係数操作を示す。

 R_0 = Q_0
 R_{n+1} = Q_n
 R_i = \frac{i}{n+1}Q_{i-1} + (1-\frac{i}{n+1})Q_i, 1 \leq i \leq n

Bezier曲線の和と差

 A(t), B(t), C(t) をそれぞれBezier曲線とする。
係数をそれぞれ Q_i, R_i, S_iとする。
 C(t) = A(t) \pm B(t) となるような C(t)の係数を示す。

AとBの次数が等しい場合

 S_i = Q_i \pm R_i
そのまま。複号は同順である。

AとBの次数が異なる場合

次数の低いBezier曲線を次数上げにより係数操作する。
あとは次数が等しい場合と同様。

Bezier曲線の積

 A(t), B(t), C(t) をそれぞれBezier曲線とする。
次数をそれぞれ  l,m,n とする。
係数をそれぞれ  Q_i, R_j, S_k とする。
 C(t) = A(t) \times B(t) となるような C(t)の係数を示す。
ベクトルの積はいろいろあるが、結合則・分配則を満たすような積であればよい。
よく使う積は普通に使える: 要素ごとの積, スカラー倍, 内積, クロス積

 S_k = \sum_{i,j,i+j=k} Q_i \times R_j \frac{{{l}\choose{i}}{{m}\choose{j}}}{{n}\choose{k}}
 n = l + m

添字が煩雑だが、 Q_i, R_jのすべての組に対して右辺の計算を S_k, k = i + jに蓄積する。

交線追跡法

曲面と曲面の交線を求める。
(光線追跡法(ray tracing)ではない。)

曲面と平面の交線

交線は曲面上のパラメータとする。
曲面と平面の幾何的な関係から微分方程式を立てて求める。

曲面  S = S(u,v)
平面  \Pi = \{ N \cdot (X - P) = 0 | X \in R^3 \}
交線  C = C(t) = (u(t),v(t))
実際の交線  \Gamma = S(C(t))
平面は陰関数表現とし、
 N: 法線ベクトル
 P: 通過点
である。

曲面上のパラメータとして、ある交点  X_0 = C(t_0) = (u_0, v_0) を考える。
曲面と平面の式に代入すると、
 N \cdot (S(X_0) - P) = 0
である。
交線のパラメータで微分する。
 N \cdot (S_u u^\prime + S_v v^\prime) = 0
分解して、以下の微分方程式が得られる。
 C^\prime = (u^\prime, v^\prime) = (\pm N \cdot S_v, \mp N \cdot S_u)
(uとvの添字がひっくり返っているのが正しい。)
符号は交線をどちら向きに追跡するかに相当する。

線長パラメータ

パラメータを線長パラメータで正規化すると扱いやすい。
実交線のパラメータでの微分
 \Gamma^\prime = S_u u^\prime + S_v v^\prime
であるので、これを用いて交線を正規化する。
 \dot{C} = \frac{C^\prime}{\sqrt{\Gamma^\prime \cdot \Gamma^\prime}}
線長パラメータでの微分 \dot{C}と表記している。
分母のルートの中身は第1基本形式である。展開すると以下のようになる。
 \Gamma^\prime \cdot \Gamma^\prime = S_u \cdot S_u {u^\prime}^2 + 2 S_u \cdot S_v u^\prime v^\prime + S_v \cdot S_v {v^\prime}^2
 = E {u^\prime}^2 + 2 F u^\prime v^\prime + G {v^\prime}^2
 E,F,Gはそれぞれ以下の通りである。
 E = S_u \cdot S_u
 F = S_u \cdot S_v
 G = S_v \cdot S_v

曲面と曲面の交線

TODO

オフセット曲面の方向微分

オフセット曲面のUV方向微分を求める。

表記

以下のように表記する。
内積  A \cdot B
外積  A \times B
曲線  C = C(t)
曲線の微分  C^\prime = \frac{{\mathrm d}C}{{\mathrm d}t}(t)
曲面  S = S(u,v)
曲面のU方向微分  S_u=\frac{\partial S}{\partial u}(u,v)
曲面のV方向微分  S_v=\frac{\partial S}{\partial v}(u,v)

正規化法線ベクトルのUV方向微分

法線ベクトル  N_0= S_u \times S_v
法線ベクトルのU方向微分  N_{0u}=S_{uu} \times S_v + S_u \times S_{uv}
法線ベクトルのV方向微分  N_{0v}=S_{uv} \times S_v + S_u \times S_{vv}
法線ベクトルの長さ  l = \sqrt{N_0 \cdot N_0}
正規化法線ベクトル  N = \frac{N_0}{l}
正規化法線ベクトルのU方向微分  N_u = \frac{1}{l} N_{0u} - \frac{N_0 \cdot N_{0u}}{l^3} N_0
正規化法線ベクトルのV方向微分  N_v = \frac{1}{l} N_{0v} - \frac{N_0 \cdot N_{0v}}{l^3} N_0

オフセット曲面のUV方向微分

オフセット量  rとする。
オフセット曲面  R = S + r N
オフセット曲面のU方向微分  R_u = S_u + r N_u
オフセット曲面のU方向微分  R_v = S_v + r N_v

C言語のライブラリをC++からテストするようにしてみた

C言語で書かれているライブラリがあって、バグがポコポコ出てくるのでなんとかしてテストを仕込みたいと思った。その顛末を書く。
このCのライブラリはそのままC++でも通るコードなのであるが、バージョン管理がVSSであるためファイル名を変えたりすると履歴が吹っ飛ぶ。*1わりと仕方なくCで運用されている。
手動で後片付けとか書きたくないしマクロの嵐とかこわいしgoogletestとか便利なのでC++からこれを呼び出すことは決めていた。

非static関数をテストできるようにした

ライブラリは非static関数とstatic関数からなる。翻訳単位内でしか使わない関数はだいたいstaticにしてある。
外部からアクセスされる非static関数のテストはまあ簡単にできた。C++側ではマングリングを回避するためにextern "C"をつける必要がある。

// hoge.c
int fuga( int x );
static int static_hoge( int x ) {
  return fuga( x );
}
int hoge( int x ) {
  return static_hoge( x );
}
// fuga.c
int fuga( int x ) {
  return x;
}
// hoge_test.cpp
#include <gtest/gtest.hpp>
extern "C" int hoge( int x ); // マングリングを回避するためのextern "C"
TEST( hoge, hoge ) {
  EXPECT_EQ( hoge(1), 1 );
}

libhogeをリンクする必要がある。*2
十分に小さい規模の関数群なら非static関数だけテストしておけば事足りるかもしれない。
しかしあんまりテストされていないstatic関数がたくさんある場合は心許ない。

static関数をテストできるようにした

通常はあんまりやらない方法をテストの時だけ行うということはよくある。
includeは通常はヘッダファイルを貼付けるために使うが、Cファイルを直接貼付けることも普通にできる。

// hoge_test.cpp
extern "C" {
#include "hoge.c"
}
#include <gtest/gtest.hpp>
TEST( static_hoge, hoge ) {
  EXPECT_EQ( static_hoge(1), 1 );
}

だいたいコンパイルは通ると思う。必要に応じて調整を行う。*3
さて、コンパイルは通るけれどもリンクが通らない。
libhogeをリンクしてしまうとhogeが多重定義でエラーとなる。一方、リンクしなければfugaが未定義でエラーとなる。
そこでライブラリ側のすべてのCファイルを、対応するC++ファイルに上記の要領でincludeし、libhogeなしでリンクする。

// fuga_test.cpp
extern "C" {
#include "fuga.c"
}
// another_test.cpp
extern "C" {
#include "another.c"
}

...

ファイルが10個を超えると脳みそ腐るのでスクリプトか何かで自動化したほうがよい。*4
そんなこんなでだいぶテストが書きやすくなった気がする。

*1:当然のことながらローカルでは自分用Gitで管理している。めんどくさいけどVSS単体よりかはだいぶまし。

*2:通常の使い方なので通常通り。

*3:不思議なdefineがC内で行われている場合はそっとundefするなど。

*4:実際にやる気なくなったので実証済み。とっととスクリプト書けよという。

C#の動的ロードメモ

クライアント(AppDomainTest)

class Program
{
  static void call()
  {
    AppDomain ad = null;
    try
    {
      ad = AppDomain.CreateDomain("TEST");
      var handle = ad.CreateInstance("ClassLibrary1", "ClassLibrary1.Class1");
      int res = ((IClass1)handle.Unwrap()).test();
      System.Console.WriteLine(res);
    }
    finally
    {
      AppDomain.Unload(ad);
    }
  }
  static void Main(string[] args)
  {
    while (true)
    {
      call();
      System.Threading.Thread.Sleep(1000);
    }
  }
}
public interface IClass1
{
  int test();
}

サーバ(ClassLibrary1)

public class Class1 : MarshalByRefObject, AppDomainTest.IClass1
{
  public int test()
  {
    return 10;
  }
}

こんなかんじでクライアントを動かしたままサーバを書き換えたりできるんじゃないかな。