More servicesWindows Live
HomeHotmailSpacesOneCare
 
MSN
Sign in
 
 
Spaces home  ※ 画面は開発中のものですPhotosProfileFriendsMore Tools Explore the Spaces community

※ 画面は開発中のものです

いろいろと作ってます。 プログラミング( VC++, C#, Javascript, PHP ) 時々ノイズ(鉄道 庭のビワ 他)

TKYM

View spaceSend a message
Occupation:
Age:
Location:
Interests:
PCいじくってます。
VC++でプログラムを作るのがメイン。
Webは Javascript + PHP

ソフ開の勉強をします。
簿記2級もまだ目指してます。

鉄道が好きです。
時刻表をよく見ます。
模型は金銭的、撮影は時間的な問題から実践できていません。

2007年、庭にビワの種をまきました。
July 04

C# でベジエ曲線(3)

計算が複雑になってきてきた。そこで、多項式の計算をクラスにしてみた。
四則演算と微分・積分を実装した。
ただし、 1/x の積分は出来ない。 とか、速度の問題とかいろいろとある。

カーブ半径を取得したい。
曲線の曲がり具合は、曲率というもので表されるらしく、
一般に用いる半径は曲率半径といって曲率の逆数らしい。

したがって、曲率を求めたい。が、
http://ja.wikipedia.org/wiki/%E6%9B%B2%E7%8E%87
わからない。

検索して調べていると

(x’y’’ – x’’y’ ) / √{({x’}^2 + {y’}^2)^3}

x(t), y(t) : 曲線上の点
x’(t), y’(t) : 接線の傾き

こんな式を見つけたのでそのまま計算して逆数にすると、正しいらしい結果が出てきた。image

カーブ半径に合わせて円を描いてみた。


下がコードの一部。 tmp1 が上式の 分子、 tmp2 が分母。

       public float curvature( float t) {
            float tmp1 = -f_tangental_x.differentiate().GetValue(t) * f_tangental_y.GetValue(t) +
                            f_tangental_y.differentiate().GetValue(t) * f_tangental_x.GetValue(t);
            float tmp2 = (float)Math.Sqrt((double)powFI((powFI(f_tangental_x.GetValue(t), 2) 
                            + powFI(f_tangental_y.GetValue(t), 2)), 3));
            if (tmp2 == 0) return 0;
            return  (tmp1 / tmp2);
        }

differentiate は多項式クラスで微分をするメソッドで、 (x^a)’ = ax^(a-1) をすべての項に対して行う。
GetValue は多項式の変数に代入し、値を得るもの。

        //微分する
        public polynomial differentiate() {
            polynomial result = new polynomial();

            foreach (KeyValuePair<float, float> kpy in this.values)
            {
                if (kpy.Key != 0)
                    result.values[kpy.Key - 1.0f] = kpy.Key * kpy.Value;
            }

            return result;
        }

多項式クラスでは、各項を、キーを指数、値を係数とするリストに保存している。

 

正確さや速度に問題が出てくるかもしれないが、機能としてはこれくらいかと思う。
次はカーブをつなげたレールの設計とか。

July 01

夏が近づく / C#

ビワを食べた。
家族には不人気のビワ、好きなのは自分だけらしい。
おいしかったからこれも植えてしまおうか。

DSC00791

トカゲを見た。 トカゲと呼んでいるがこれはカナヘビのような気がする。
http://ja.wikipedia.org/wiki/%E3%82%AB%E3%83%8A%E3%83%98%E3%83%93
DSC00780 
DSC00781 
飼いたいが餌は虫。しかも手ごろなデュビアというムシがなんと ゴキの仲間。
無理だ。

C# の機能を一通り勉強。 しかし ・・・ 情報が古い。
とりあえず気にせずに コーディング。

June 29

C# でベジエ曲線(2)

無事、ベジエ曲線とその並行曲線がかけたのが前回まで。

曲線の式が分かることで、列車を滑らかにカーブさせることができそうだが、
まだ曲線の上を一定速度で動かすことができない。
前回のベジエ曲線の式は、パラメータ t で表されていて、
t を一定に増加させても 等速度で動いてくれないように見える。

だから、 適当な距離 l において、パラメータ t はいくつなのか調べないといけない。
また、曲線全体の距離も知りたい。

そこで、高校の数学教科書の最後のほうのページに、「曲線の長さ」という項目があったことを思い出し、開いてみた。

(b~a の定積分)∫√[ { f’(t) }^2  +  { g’(t) }^2 ] dt

次数の高い多項式がルートの中に入ったものを積分するのは非常に難しそう。
つい最近、大学で積分の数値計算を習ったことから、それで計算することにする。

とはいっても、積分は基本的に、地道に面積を計上していくただのループ。
なんとかならないものかという気持ちが残る。

--------------

積分のアルゴリズムには、台形公式とシンプソンの公式と、ほかに各点の重みや積分区間を不等間隔にする何とかという公式があるらしい。

あるグラフを区間ごとに区切って面積を計算する時、その区間のグラフが曲線でないこととして計算すれば、台形公式になる。 それでは誤差が大きくなってしまうので、 区間上の3点を通る2次曲線の積分として考えれば、少しは誤差は小さくなる。これがシンプソンの公式である。

他にもいい方法があると本には書いてあるが、難しくなると面倒だし、ピクセル単位であっていればいいのでシンプソン法で計算してみることにした。

------------

とりあえず、 曲線の パラメータ t での長さ l が何ピクセルなのかを知りたい。

計算のやり方は教科書のまる写しでなんとかする。
グラフを、 (t0, l0), (t1, l1), (t2, l2),,,,,(tN, lN) と区切って、
(h/3)(l0 + 4l1 + l2) + (h/3)(l2 + 4l3 + l4) + … と計算していく。

これを簡単にするとこんな感じ。
(h/3) * {
l0 + lN
+ l偶数番 * 2
+ l奇数番 * 4
}

ちなみに、区間は偶数個に区切らなければならないらしい。

        //パラメータ t を 開始点からの距離 l に変換する h は積分刻み幅
        //シンプソンの公式を用いた
        static public float t2l(float t, PointF[] pt, float h)
        {
            float tcnt, prev =0.0f, tmplen, tmpadd, total = 0.0f;
            int i;
            for (tcnt = 0.0f, i = 0; tcnt <= t; i++, tcnt += h) {
                tmplen = length(bezier.tangental_func(pt, tcnt));
 
                if (tcnt + h > t) 
                { //最後
                    if (i % 2 == 1)
                    { //奇数個で終わる
                        tmpadd = (prev + tmplen * 4 + length(bezier.tangental_func(pt, tcnt + h))) / 2;
                    }
                    else
                    { //偶数個で終わる
                        tmpadd = tmplen;
                    }
                }
                else if (tcnt < h)
                { //最初:そのまま
                    tmpadd = tmplen;
                }
                else if (i % 2 != 0)
                { //奇数
                    tmpadd = tmplen * 4;
                }
                else
                { //偶数
                    tmpadd = tmplen * 2;
                }
                total += tmpadd;
                prev = tmplen;
            }
            return h/3.0f * total;
        }

積分刻み幅を任意に定められることにしたかったので、項数が奇数になってしまった場合、区間を超えて計算し、超えた区間を2で割るようにしてみた。

そして、その逆変換

        //開始点からの距離 l を パラメータ t に変換する h は積分刻み幅
        //シンプソンの公式
        static public float l2t(float l, PointF[] pt, float h)
        {
            float tcnt, prev =0.0f, tmplen, tmpadd, total = 0.0f, lcnt, endlen = 0.0f;
            int i;
            for (tcnt = 0.0f, i = 0; tcnt <= 1.0f && endlen < l; i++, tcnt += h) {
                tmplen = length(bezier.tangental_func(pt, tcnt));
 
                //ここで終了した場合の長さ
                if (i % 2 == 1)
                { //奇数個で終わる
                    endlen = (total + (prev + tmplen * 4 + length(bezier.tangental_func(pt, tcnt + h))) / 2)
                        * (h / 3.0f);
                }
                else
                { //偶数個で終わる
                    endlen = (total + tmplen)
                         * (h / 3.0f);
                }

                //加算処理
                if (tcnt < h)
                { //最初
                    tmpadd = tmplen;
                }
                else if (i % 2 != 0)
                { //奇数
                    tmpadd = tmplen * 4;
                }
                else
                { //偶数
                    tmpadd = tmplen * 2;
                }

                total += tmpadd;
                prev = tmplen;
            }
            return tcnt - h/2.0f;
        }

距離から t を求める場合、ループするたびにその地点での距離を知りたいので、そのつど終了した場合の距離を計算し、終了判断しなければならない。 あと、戻り値は必ず 本来の値以上になることから、 刻み幅の半分を引いてみた。

image

t = 0.5 の位置を距離に変換し、戻してみた。
大体あっているが、実用的なのか微妙なところ。

 

それより、コードがなんか汚い。 言い訳としては、ご丁寧に中かっこをエディタが改行してくれるということ。
IntelliSense も C++ に比べるとおせっかいに働いてくれる。

C# でベジエ曲線

C# を始めた。
Visual studio にはいくつかの言語が入っていて、 VB とかもできるのだが 手は伸びていなかった。
C を使っていると 効率が悪いということにいい加減気づくべきではないかと思い、 C# を始めてみようと思ったわけです。

#include の順番とか、 定義の順番だとか、ヘッダファイルとか、悩まされることが少ない。
メモリの確保とかも考えるべきことがない。

C++はC++で深い。 Windows の深いところまで勉強するには C++ が必須。
でも、普通のソフトを作るのに手間をかけすぎていては困る。器用に使い分けてみたい。

-----------------

C# で作りたいものは、ちょっとしたゲームのようなもの。
鉄道系のシミュレーションが好きなので、それの簡易版っぽいものを考えている。
それで、そのレールをベジエ曲線にしてその上をガタゴト走らせようと考えている。

ただ、単純にベジエ曲線を 便利なライブラリにおそらく入っているであろう DrawBezier などという関数でビーッと引くだけではすまないであろう。その線に沿って、列車が動かなければならない。

となると、数式でその座標を表さなければならない。
なので、数式とにらめっこする必要が生じてくる。

----------------

調べてみると、
http://ja.wikipedia.org/wiki/%E3%83%99%E3%82%B8%E3%82%A7%E6%9B%B2%E7%B7%9A
こんな感じの数式であることがわかった。

∑ Bi Jni(t)

Jni(t) = (n i) t^i (1-t)^(n-i)

(n i) という部分をベクトルかと思ってずっと考えていたらこれは nCi (組み合わせ)ということが判明。
http://ja.wikipedia.org/wiki/%E4%BA%8C%E9%A0%85%E5%AE%9A%E7%90%86
(n i) というのは二項係数というらしい。最初からそう書いてあればいいのに。

とりあえず線の式はわかった。
パラメータ t での座標を返す関数を書いてみた。制御点の数は可変にしておいた。

        static public PointF curve(PointF[] pt, float t) {
            PointF total = new PointF(0.0f, 0.0f);
            int n = pt.Length - 1;
            int i;

            for (i = 0; i < pt.Length; i++) {
                total.X += pt[i].X * mCn(n, i) * (float)Math.Pow((double)t, i) * (float)Math.Pow(1.0 - t, (double)n - i);
                total.Y += pt[i].Y * mCn(n, i) * (float)Math.Pow((double)t, i) * (float)Math.Pow(1.0 - t, (double)n - i);
            }

            return total;
        }

でも1本だけでは足りない。平行にもう1本ほしい。レールだから。

曲線の接線に対して直角に、一定距離進んだところをつないで線にすれば平行になりそう。
接線を求めるには式を微分すればよさそう。

Bi と(n i) を定数として、 { (t^i) * (1-t)^(n-i)} ’  の部分を考える。
単純に考えると
(t^i)’ * (1-t)^(n-i)  +  t^i * {(1-t)^(n-i)}’ 積の微分
it^(i-1) * (1-t)^(n-i)  +  t^i * (n-i)(1-t)^(n-i-1)(-1) 合成関数

よって ∑ Bi (n i) { it^(i-1) * (1-t)^(n-i)  +  t^i * (n-i)(1-t)^(n-i-1)(-1) }

という感じになりそうなのだが、このままコードにすると失敗した。
0乗のときは1なので微分するとそのまま消えないといけないから。

        //ベジエ曲線の、接線を表す関数
        public static SizeF tangental_func(PointF[] pt, float t)
        {
            PointF total = new PointF(0.0f, 0.0f);
            int i;
            int n = pt.Length - 1;

            for (i = 0; i < pt.Length; i++)
            {
                float d1 = (i == 0) ? 0.0f : (float)i * powFI(t, i - 1);
                float d2 = (n - i == 0) ? 0.0f : (float)(n - i) * powFI(1.0f - t, n - i - 1) * (-1);

                total.X += pt[i].X * mCn(n, i) *
                (
                    d1 * powFI(1.0f - t, n - i) + powFI(t, i) * d2
                );


                total.Y += (pt[i].Y * mCn(n, i) *
                (
                    d1 * powFI(1.0f - t, n - i) + powFI(t, i) * d2
                ));
            }
            return new SizeF(total.X, total.Y);
        }

* powFI は float 型をキャストせずに べき乗できるようにした単純な関数。
* d1 と d2 がそれぞれ t^i と (1-t)^(n-i) を微分したものをいれる変数。

この戻り値をそのまま描画したらうまく接線が描画された。
しかし、場所によって長さが変わってしまった。 曲線の パラメータ t の 1あたりの長さになってしまっているかららしい。

よって、このx, y に同じ値 l をかけて 長さを1にする必要がある。
そうしないと並行曲線が描けない。

(lx)^2 + (ly)^2 = 1^2
l^2 = 1 / (x^2 + y^2)
l = 1 / √(x^2 + y^2)

となり、(x / √(x^2 + y^2) , y / √(x^2 + y^2) ) が長さ1ピクセル分のベクトルになる。
英語のwikipedia に Parallel curve という項目があって、これを見ると何となく正しいような気がする。
http://en.wikipedia.org/wiki/Parallel_curve

この値に必要な長さをかけて使えばよい。

----------

接線を直角に曲げて、一定距離の地点を線でつないで行けば平行な線が引けそうである。
接線のベクトルが (x, y) なら、 その直角は ( y, –x )。

で、レール(道路)の幅だけ掛け算する。
これで平行線のグラフもわかった。

これらを使って適当に線を引く

image

はい、完成。

ところで、 bezier 曲線は、 ベジエと発音するのがどちらかというと原語に近いとのこと。
少し早くしゃべるだけで区別がつかなくなるような気もするが。

June 17

Visual studio コマンドウィンドウ

先生は 「統合開発環境を使えば簡単ですが、 何かと応用が利かなくなったりするのでコマンドも覚えましょう。」 といいます。
 
今日、 Visual Studio の画面上に 「コマンドウィンドウ」 を発見したので、適当に dir と打ち込んで見たところ、
 
"コマンド "dir" は有効ではありません。"
との反応。
 
調べてみたところ、こんなコマンドを入力するらしいです。
 
Visual Studio コマンドの定義済みのエイリアス
 
ショートカットキーで十分そうなコマンドが多いですが、
使いそうなものをメモ。
 
  • ? x : xの内容を表示する
  • gotoln : 指定行に移動(ステータスバーの行数をダブルクリックしてもOK)
  • lcase : エディタの選択範囲を小文字にする
  • ucase : エディタの選択範囲を大文字にする
  • nav : ブラウザタブを開く
  • shell x : シェルで x を実行
  • wordwrap : 右端で折り返す(折り返さない)

エイリアスの作成方法
http://msdn.microsoft.com/ja-jp/library/xasxzd71(VS.80).aspx

View more entries
 
足跡。
View space
SILVERWOLF
View space
emus
View space
coffe.k
View space

Updated 3/28/2008
Updated 4/17/2008
Updated 6/4/2008