![]() |
|
Spaces home ※ 画面は開発中のものですPhotosProfileFriendsMore ![]() | ![]() |
※ 画面は開発中のものですいろいろと作ってます。 プログラミング( VC++, C#, Javascript, PHP ) 時々ノイズ(鉄道 庭のビワ 他)
|
||||||||||||||||||||||||||||||||||||||||
|
July 04 C# でベジエ曲線(3)計算が複雑になってきてきた。そこで、多項式の計算をクラスにしてみた。 カーブ半径を取得したい。 したがって、曲率を求めたい。が、 検索して調べていると (x’y’’ – x’’y’ ) / √{({x’}^2 + {y’}^2)^3} こんな式を見つけたのでそのまま計算して逆数にすると、正しいらしい結果が出てきた。
カーブ半径に合わせて円を描いてみた。
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) をすべての項に対して行う。 //微分する
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#ビワを食べた。 トカゲを見た。 トカゲと呼んでいるがこれはカナヘビのような気がする。 C# の機能を一通り勉強。 しかし ・・・ 情報が古い。
June 29 C# でベジエ曲線(2)無事、ベジエ曲線とその並行曲線がかけたのが前回まで。 曲線の式が分かることで、列車を滑らかにカーブさせることができそうだが、 だから、 適当な距離 l において、パラメータ t はいくつなのか調べないといけない。 そこで、高校の数学教科書の最後のほうのページに、「曲線の長さ」という項目があったことを思い出し、開いてみた。 (b~a の定積分)∫√[ { f’(t) }^2 + { g’(t) }^2 ] dt 次数の高い多項式がルートの中に入ったものを積分するのは非常に難しそう。 -------------- 積分のアルゴリズムには、台形公式とシンプソンの公式と、ほかに各点の重みや積分区間を不等間隔にする何とかという公式があるらしい。 あるグラフを区間ごとに区切って面積を計算する時、その区間のグラフが曲線でないこととして計算すれば、台形公式になる。 それでは誤差が大きくなってしまうので、 区間上の3点を通る2次曲線の積分として考えれば、少しは誤差は小さくなる。これがシンプソンの公式である。 他にもいい方法があると本には書いてあるが、難しくなると面倒だし、ピクセル単位であっていればいいのでシンプソン法で計算してみることにした。 ------------ とりあえず、 曲線の パラメータ t での長さ l が何ピクセルなのかを知りたい。 計算のやり方は教科書のまる写しでなんとかする。 これを簡単にするとこんな感じ。 ちなみに、区間は偶数個に区切らなければならないらしい。
//パラメータ 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 を求める場合、ループするたびにその地点での距離を知りたいので、そのつど終了した場合の距離を計算し、終了判断しなければならない。 あと、戻り値は必ず 本来の値以上になることから、 刻み幅の半分を引いてみた。 t = 0.5 の位置を距離に変換し、戻してみた。
それより、コードがなんか汚い。 言い訳としては、ご丁寧に中かっこをエディタが改行してくれるということ。 C# でベジエ曲線C# を始めた。 C++はC++で深い。 Windows の深いところまで勉強するには C++ が必須。 ----------------- C# で作りたいものは、ちょっとしたゲームのようなもの。 ただ、単純にベジエ曲線を 便利なライブラリにおそらく入っているであろう DrawBezier などという関数でビーッと引くだけではすまないであろう。その線に沿って、列車が動かなければならない。 となると、数式でその座標を表さなければならない。 ---------------- 調べてみると、 (n i) という部分をベクトルかと思ってずっと考えていたらこれは nCi (組み合わせ)ということが判明。
とりあえず線の式はわかった。 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本ほしい。レールだから。 曲線の接線に対して直角に、一定距離進んだところをつないで線にすれば平行になりそう。 //ベジエ曲線の、接線を表す関数
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 型をキャストせずに べき乗できるようにした単純な関数。 となり、(x / √(x^2 + y^2) , y / √(x^2 + y^2) ) が長さ1ピクセル分のベクトルになる。 この値に必要な長さをかけて使えばよい。 ---------- 接線を直角に曲げて、一定距離の地点を線でつないで行けば平行な線が引けそうである。 はい、完成。 ところで、 bezier 曲線は、 ベジエと発音するのがどちらかというと原語に近いとのこと。 June 17 Visual studio コマンドウィンドウ先生は 「統合開発環境を使えば簡単ですが、 何かと応用が利かなくなったりするのでコマンドも覚えましょう。」 といいます。
今日、 Visual Studio の画面上に 「コマンドウィンドウ」 を発見したので、適当に dir と打ち込んで見たところ、
"コマンド "dir" は有効ではありません。"
との反応。
調べてみたところ、こんなコマンドを入力するらしいです。
Visual Studio コマンドの定義済みのエイリアス
ショートカットキーで十分そうなコマンドが多いですが、
使いそうなものをメモ。
エイリアスの作成方法
|
足跡。
|
||||||||||||||||||||||||||||||||||||||
|
|