HSPポータル
サイトマップ お問い合わせ


HSPTV!掲示板


未解決 解決 停止 削除要請

2017
1213
ソラベジェ線のような滑らかな曲線を描画したい。4解決


ソラ

リンク

2017/12/13(Wed) 13:18:20|NO.82013

制御点を自由に増減できるベジェ線を描画したいのですが、行き詰まっています。

http://rpen.blogspot.jp/2007/05/blog-post.html
まず、上記のソースコードを参考に作ってみました。
制御点が4個の場合は正常に動くのですが10個にすると形がおかしくなります。
そもそもベジェ線とはそういうものなのか、単純に作り方が悪いのか。

#module #deffunc 曲線計算 array x,array y,int s,double 平均化係数,array arr 制御点数=length(x) 制御点数_1=制御点数-1;終点は含めない。 制御点数_2=制御点数-2;端点は含めない。 定数=double(制御点数)-1.0 arr.0.0=double(x.0) arr.1.0=double(y.0) ct=1.0/s t=ct k=1.0 終点x=double(x.(制御点数_1)) 終点y=double(y.(制御点数_1)) repeat s, 1 t +ct k -ct lx=double(x.0) ly=double(y.0) 一時x=終点x 一時y=終点y repeat 制御点数_1 lx*k ly*k 一時x*t 一時y*t loop lx+一時x ly+一時y repeat 制御点数_2,1;端点は含めない。 一時=定数 repeat 制御点数_1-cnt 一時*k loop repeat cnt 一時*t loop lx+(一時*x.cnt) ly+(一時*y.cnt) loop arr.0.cnt=lx arr.1.cnt=ly loop return #global #uselib "winmm.dll" #cfunc timeGetTime "timeGetTime" screen 0,1000,500 制御点数=105;曲線を制御する支点の数 品質=200;曲線の品質 平均化係数=0.25;どの程度平均化するか 試行回数=100;時間測定の精度 円半径=5;描画用 ddim arr,2,品質;曲線の計算結果を代入する変数 ;とりあえずランダムに作る。 repeat 制御点数 x.cnt=(1000/制御点数)*cnt y.cnt=rnd(480)+10 loop ;処理時間を測定。 開始時間=timeGetTime() repeat 試行回数 曲線計算 x,y,品質,平均化係数,arr loop title "1回あたり"+(double(timeGetTime()-開始時間)/試行回数)+"ms" repeat getkey k mx=mousex my=mousey redraw 0 ;ここから描画やドラッグの処理。 color 255,255,255:boxf color 0,0,0 if k=1 &k_bac=0:フラグ=0 repeat 制御点数 circle x.cnt-円半径,y.cnt-円半径,x.cnt+円半径,y.cnt+円半径 if limit(mx,x.cnt-円半径*4,x.cnt+円半径*4)=mx & limit(my,y.cnt-円半径*4,y.cnt+円半径*4)=my & k=1 &k_bac=0:クリックct=cnt:フラグ=1 loop if k=1&フラグ=1:{ x.クリックct=mx y.クリックct=my } ;ここまで。 曲線計算 x,y,品質,平均化係数,arr;曲線を計算 ;描画 pos arr.0.0,arr.1.0 repeat 品質 line arr.0.cnt,arr.1.cnt loop redraw 1 k_bac=k await loop

もう1種類、独自にやってみたのですが処理に時間がかかります。
2段階にわけて処理をしているのが原因だと思いますが、
これをまとめるにはどうすればいいか・・・


#module #deffunc 曲線計算 array x,array y,int s,double 平均化係数,array arr 制御点数=length(x) 制御点数_1=制御点数-1 平均化範囲=int(平均化係数*s) t=double(制御点数)/s ddim arr2,2,s ;間を直線的に補完して一時変数にいれる。 repeat s ct_0=t*cnt ct_1=int(ct_0) ct_2=ct_0-ct_1 ct_3=1.0-ct_2 arr2.0.cnt=(ct_3*x.ct_1)+(ct_2*x.limit(ct_1+1,0,制御点数_1)) arr2.1.cnt=(ct_3*y.ct_1)+(ct_2*y.limit(ct_1+1,0,制御点数_1)) loop ;端点だけ平均化を適用させない。 arr.0.0=double(x.0) arr.1.0=double(y.0) arr.0.(s-1)=double(x.制御点数_1) arr.1.(s-1)=double(y.制御点数_1) ;一時変数の値を平均化してarrに代入。 repeat s-2,1 ct=cnt arr.0.ct=0.0 arr.1.ct=0.0 repeat 平均化範囲 arr.0.ct+arr2.0.limit(ct-cnt,0,s-1)+arr2.0.limit(ct+cnt,0,s-1) arr.1.ct+arr2.1.limit(ct-cnt,0,s-1)+arr2.1.limit(ct+cnt,0,s-1) loop arr.0.ct/(平均化範囲*2) arr.1.ct/(平均化範囲*2) ;dialog ""+arr.0.ct+","+arr.1.ct+"" loop return #global #uselib "winmm.dll" #cfunc timeGetTime "timeGetTime" screen 0,1000,500 制御点数=4;曲線を制御する支点の数 品質=200;曲線の品質 平均化係数=0.25;どの程度平均化するか 試行回数=100;時間測定の精度 円半径=5;描画用 ddim arr,2,品質;曲線の計算結果を代入する変数 ;とりあえずランダムに作る。 repeat 制御点数 x.cnt=(1000/制御点数)*cnt y.cnt=rnd(480)+10 loop ;処理時間を測定。 開始時間=timeGetTime() repeat 試行回数 曲線計算 x,y,品質,平均化係数,arr loop title "1回あたり"+(double(timeGetTime()-開始時間)/試行回数)+"ms" repeat getkey k mx=mousex my=mousey redraw 0 ;ここから描画やドラッグの処理。 color 255,255,255:boxf color 0,0,0 if k=1 &k_bac=0:フラグ=0 repeat 制御点数 circle x.cnt-円半径,y.cnt-円半径,x.cnt+円半径,y.cnt+円半径 if limit(mx,x.cnt-円半径*4,x.cnt+円半径*4)=mx & limit(my,y.cnt-円半径*4,y.cnt+円半径*4)=my & k=1 &k_bac=0:クリックct=cnt:フラグ=1 loop if k=1&フラグ=1:{ x.クリックct=mx y.クリックct=my } ;ここまで。 曲線計算 x,y,品質,平均化係数,arr;曲線を計算 ;描画 pos arr.0.0,arr.1.0 repeat 品質 line arr.0.cnt,arr.1.cnt loop redraw 1 k_bac=k await loop

1つめのソースコードを改良してキレイなベジェ線を書くのが理想的なのですが、
自分では間違いを見つけることができませんでした。
申し訳ありませんが、皆様の知恵をお貸しいただけないでしょうか。
宜しくお願いします。



この記事に返信する


ぜーっと!

リンク

2017/12/16(Sat) 13:51:39|NO.82036

サンプルを動作させると、フリーズして応答なしになるので見れませんでした。
誰も返答無いようだし、私の説明できる範囲で(^^;

アドビのイラストレーターなどを触ると分かるのですが
基本的に1つの座標点から生える制御点は2つです。
2つのうち、1つはその点から先の曲線(次の点)に影響を与える制御点。
もう1つは、その点から前の曲線(前の点)に影響を与える制御点です。
なので、1つの曲線には多くても制御点が2つあれば十分でしょう。

それ以上に制御点を(プログラム的には)増やすこともできるのでしょうが、さらに曲線を増やしたい場合
制御点ではなく、座標点を増やす考え方の方が良いのではないでしょうか。

私もゲームにペジェ曲線を取り入れてみたいと、つい先日ネットを検索していたので
個人的にはタイムリーな話題でした。(^^



3k

リンク

2017/12/16(Sat) 22:33:41|NO.82042

あぁ、ベジェ曲線と聞いたので普通によくあるコンピュータグラフィクス用途の3次ベジェ曲線の方かと思ったんですが、
本来の定義のN次のベジェ曲線の方なんですね。なるほど、面白そうですね。

とりあえず、ベジェ曲線は定義式を見れば分かる通り両端の点を通る曲線なので、少なくとも1つ目に提示して頂いたコードは結果が間違っています。

2つ目の方は両端は通ってますが、これだけ制御点が多い状況で一つの制御点を修正した際に曲線が動く範囲が広すぎますし、
制御点の分布に従ってカーブが緩やかに繋がるのが特徴となる曲線なので、何か違う…気がします。。。
(…実はN次のベジェ曲線は見たことないので正確な事は言えないです(笑))

ひとまず1つ目のコードはどこが間違っているんだろうかと追おうと思ったんですが、
日本語が入っているコードは精神的に受け付けなかったので、すみませんがどこが悪いかは分からないです…。
(ごめんなさい、普段そういうコード書いてないからだとは思うのですが、読んでも本当に何も頭に入ってこなかったので、自分でも少し驚いてます…)

2つ目も同様に読めなかったので、ひとまずこちらからお力になれるのはこれしかないと、
シンプルにベジェ曲線を求めるコードを書いてみました。

これもフルスクラッチなので合ってる保証はないですが、ほぼ定義通りに書いているのと、
普段イラレ等で使っているベジェ曲線の感覚からするとでこれかなぁ、という感触です。

コード的に最適化の余地はかなり有りますが、今の着眼点はそこではないので速度は置いといて、
リファレンス結果としては有用ではないかと思ってます。


//------------------------------------------------------------------------------ //------------------------------------------------------------------------------ #module // ベジェ曲線のt[0, 1]の座標 #deffunc bezier var p_out_x, var p_out_y, int p_control_point_num, array p_control_point_x, array p_control_point_y, double p_t, local l_n p_out_x = 0.0 : p_out_y = 0.0 repeat p_control_point_num l_n = ipow(p_t, cnt) * ipow(1.0 -p_t, p_control_point_num -1 -cnt) * binomialCoef(p_control_point_num -1, cnt) p_out_x += l_n * p_control_point_x(cnt) p_out_y += l_n * p_control_point_y(cnt) loop return // yがint固定の累乗 #defcfunc ipow double p_x, int p_y, local l_res // この辺適当 if (p_y <= 0) : return 1.0 // ループる l_res = 1.0 : repeat p_y : l_res *= p_x : loop return l_res // 二項係数 #defcfunc binomialCoef int p_n, int p_k, local l_res // やばそうなのは適当に弾く if (p_k <= 0) : return 1.0 // ちゃんと計算 l_res = 1.0 repeat p_k l_res *= double(p_n - cnt) /double(p_k - cnt) loop return l_res #global //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ #define SCR_WIDTH (1000.0) #define SCR_HEIGHT (480.0) screen 0, SCR_WIDTH, SCR_HEIGHT control_point_num = 105// 制御点の数 division = 200// 分割数 // ランダムに制御点を生成する ddim control_point_x, control_point_num ddim control_point_y, control_point_num repeat control_point_num control_point_x(cnt) = (SCR_WIDTH / control_point_num) * cnt control_point_y(cnt) = rnd(SCR_HEIGHT) + 10.0 loop // 可視化 circle_radius = 4.0 dragging_point_idx = -1 repeat // ドラッグしたり getkey ml, 1 mx = double(mousex) : my = double(mousey) if (((ml ^ prev_ml) != 0) && (ml != 0)) { dragging_point_idx = -1 repeat control_point_num dx = absf(mx - control_point_x(cnt)) dy = absf(my - control_point_y(cnt)) dl2 = dx*dx + dy*dy if (dl2 < circle_radius*circle_radius) : dragging_point_idx = cnt loop prev_mx = mx : prev_my = my } if (dragging_point_idx != -1) && (ml != 0) { control_point_x(dragging_point_idx) += (mx - prev_mx) control_point_y(dragging_point_idx) += (my - prev_my) } prev_ml = ml prev_mx = mx : prev_my = my // ここから描画 redraw 0 color 255, 255, 255 : boxf // 線引く color 128, 128, 128 repeat control_point_num -1 line control_point_x(cnt+1), control_point_y(cnt+1), control_point_x(cnt), control_point_y(cnt) loop // 制御点描く color 0, 0, 0 repeat control_point_num circle control_point_x(cnt)-circle_radius, control_point_y(cnt)-circle_radius, control_point_x(cnt)+circle_radius, control_point_y(cnt)+circle_radius loop // ベジェ曲線描く color 255, 128, 128 cur_x = 0.0 : cur_y = 0.0 repeat division t = double(cnt) / double(limit(division-1, 1, division)) bezier cur_x, cur_y, control_point_num, control_point_x, control_point_y, t if (cnt) { line cur_x, cur_y } else { pos cur_x, cur_y } loop redraw 1 await 20 loop

詳しくないのであまりちゃんとした回答出来なくて申し訳ないですが、何かの参考になれば。



ソラ

リンク

2017/12/16(Sat) 22:54:51|NO.82043

お二人とも、返信有難うございます。
>>サンプルを動作させると、フリーズして応答なしになるので見れませんでした。
私が貼ったソースコードの「制御点数=105;曲線を制御する支点の数」の部分、105ではなく10の間違いでした。
恐らくこれと、処理時間を測定する処理に時間がかかって応答なしになったものと思われます。
以後、このようなミスがないよう気を付けます。

>>日本語が入っているコードは精神的に受け付けなかった
変数に日本語が使える言語は少ないので、やはり違和感を覚える方は多いんですかね。
皆さんが見る掲示板に貼る時は気を付けます、貴重なご意見ありがとうございました。

3kさんが貼っていただいたソースコード、さっそく実行してみました。
理想通りのベジェ線で感動しています。
こういうベジェ線はN次ベジェ線というんですね、勉強になりました。

皆さんのおかげで目的は達成できたので、この質問は解決とします。
お二人とも、本当にありがとうございました。



ぜーっと!

リンク

2017/12/17(Sun) 10:10:36|NO.82046

>ソラさん
サンプル提示の際は、それが動作できないと回答率下がるような気がしたので(^^;

>3kさん
おお、なるほどーN次ベジェになるとこうなるんですね(^^
やっと3次ベジェの表現まで理解できつつあるところなので助かります。

あと、日本語変数名は確かに違和感がありますね。



ONION software Copyright 1997-2023(c) All rights reserved.