|
|
2016/3/20(Sun) 23:32:01|NO.74996
今、二次元の平面や三次元空間にグラフや面を描ける、数学に特化したソフトを作っています。
平面における媒介変数を用いたもの等は割とすぐに出来ました。
ですが平面においての極方程式の計算方法と、空間においての曲面の描き方がわかりません。
教えてください。お願いします。
曲面の描き方は陽関数や陰関数、媒介変数表示、極方程式の計算方法を教えていただけると幸いです。
数学の知識はある程度あります。
|
|
2016/3/21(Mon) 00:51:29|NO.75000
とりあえず、関数(プログラムの関数じゃなくて数学のです。
以後関数とあったら数学の関数とします。)には、二つの表記があります。
陽関数…y=で始まる表記。3次元の場合はほとんど使われません。
陰関数…三次元の場合だと、ax+by+cz=定数の形で表された関数のことです。
媒介変数表示はどう説明すればいいでしょうか。3次元でいえば、媒介変数をtとすると、
x=tで書かれた式
y=tで書かれた式
z=tで書かれた式
という形で表された関数のこととでもいえばわかりますか?
で、極方程式の計算方法の説明は大変なので、
とりあえず、簡単なところだけ。
rは原点からの距離
θはX軸から正の向きにはかった角度を表します。
なので極方程式は、簡単にはグラフに出来ません。
グラフにするなら、rとθを決めた後で、
x=rcosθ
y=rsinθ
という式から、XとYを決めて、点を打つ必要があります。
|
|
2016/3/21(Mon) 09:13:10|NO.75001
余談ですが、
極方程式で使う座標系を極座標。
普段使う座標系を直交座標と言います。
(たまに、普段使う座標系をxy座標と説明してるのを見つけますが、それは間違いです。
計算の都合上、xy以外の軸を使う場合もありえるので)
|
|
2016/3/21(Mon) 09:25:51|NO.75003
>陰関数…三次元の場合だと、ax+by+cz=定数の形で表された関数のことです。
平面に限定するのですか?一般には f(x,y,z)=0 の形で表現されるものだと思います。
>曲面の描き方は陽関数や陰関数、媒介変数表示、極方程式の計算方法を教えていただけると幸いです。
陰関数のグラフを描く方が陽関数や媒介変数表示[x,y,z](t)のものを描くよりずっと大変だと思います。
陽関数はkanamaruさんが極座標系で説明した感じで比較的簡単に描けるのではないでしょうか?
|
|
2016/3/22(Tue) 00:20:16|NO.75019
回答ありがとうございます。
motchyさんの言うとおり数式処理をし、それをグラフに反映させるというプログラムを作っています。
#include "d3m.hsp"
#const double CAMERA_R 300 // 極座標のr
#const double PI 3.1415 // 円周率
camera_theta = 0.0 // 極座標のθ
camera_phi = PI / 3.0 // 極座標のφ
old_mx = 0 // 前回処理時(約50ミリ秒前)のマウスカーソルの座標
old_my = 0 // 前回処理時(約50ミリ秒前)のマウスカーソルの座標
#include "hspmath.as"
screen 0,800,600,4
di = 1.0
pai = 3.1415926535
e = M_E
*main
redraw 0
gosub *move_camera //カメラ操作
color:boxf:gosub *draw_axes //座標軸
//関数出力部
i = 0.0
repeat 2000
d1 = pai / 180.0 * i
x1 = 0.5 * powf(e,0.3*d1) * cos(d1)
y1 = 0.5 * powf(e,0.3*d1) * sin(d1)
z1 = 0
d2 = pai / 180.0 * (i + 1)
x2 = 0.5 * powf(e,0.3*d2) * cos(d2)
y2 = 0.5 * powf(e,0.3*d2) * sin(d2)
z2 = 0
color 255,0,255:d3line x1,y1,z1,x2,y2,z2
i++ loop
wheel = mousew
if (di < 65.0)&(wheel < 0):di += 0.009
if (di > 1.0)&(wheel > 0):di -= 0.009
if (di <= 1.0)&(di > 0.001)&(wheel > 0):di -=0.009
if (di <= 1.0)&(di > 0.001)&(wheel < 0):di +=0.009
title strf( "θ:%f / ", camera_theta ) + strf( "φ:%f", camera_phi )
redraw 1
await 5
goto *main
*move_camera //カメラ操作
getkey left_click, 1
if left_click {
if old_click {
camera_theta += 0.01 * ( old_mx - ginfo_mx )
camera_phi = limitf( camera_phi + 0.01 * ( old_my - ginfo_my ), 0.01, PI - 0.01 )
}
old_mx = ginfo_mx
old_my = ginfo_my
}
old_click = left_click
d3vrotate camera_x, camera_y, camera_z, 0, 0, CAMERA_R, 0, 1, 0, camera_phi
d3vrotate camera_x, camera_y, camera_z, camera_x, camera_y, camera_z, 0, 0, 1, camera_theta
d3setcam camera_x * di, camera_y * di, camera_z * di
return
*draw_axes //5px = 1
color 0,255,0
d3mes "X", 110, 0, 0
d3mes "Y", 0, 110, 0
d3mes "Z", 0, 0, 110
color 127,255,255 //5pxで「1」
d3mes "10", 50, 0, 0:d3mes "-10", -50, 0, 0
d3mes "10", 0, 50, 0:d3mes "-10", 0, -50, 0
d3mes "10", 0, 0, 50:d3mes "-10", 0, 0, -50
d3mes "50", 250, 0, 0:d3mes "-50", -250, 0, 0
d3mes "50", 0, 250, 0:d3mes "-50", 0, -250, 0
d3mes "50", 0, 0, 250:d3mes "-50", 0, 0, -250
color 0,255,0
d3arrow 1000, 0, 0, -1000, 0, 0
d3arrow 0, 1000, 0, 0, -1000, 0
d3arrow 0, 0, 1000, 0, 0, -1000
return
とりあえずスクリプトから一部抜粋したのですがどうでしょうか。今のところユーザーに関数を打ち込んでもらうことも頭にはありますが、まずは自身で登録した関数を出力できるようにという感じです。
関数出力部はなるべくどんな関数でも対応できるように漸化式のような形式(連続である関数しか出力は出来ないと思いますが)です。このような感じで三次元にも拡張していけたらと思っています。
数学の知識は高3くらいまでしかないので(私自身が高1ですので)三次元の拡張については資料を色々と読みあさってどんな感じかはなんとなくつかめました。皆様のおかげで平面においては何とかなりそうです。
スクリプトをみてわかるとおり関数出力部は媒介変数表示になっています。そして三次元に拡張していこうとすれば媒介変数が2つ必要になってしまいます。今の形をなるべく崩さずに三次元に拡張するにはどうすればいいでしょうか。
| |
|
2016/3/22(Tue) 00:24:02|NO.75020
あ、すみません。
スクリプトの関数出力部の
i++ loop
の部分のところを直しておいてください。
|
|
2016/3/22(Tue) 10:18:00|NO.75028
動かしてみました。螺旋のサンプルですか。細かいところは実際のソフトで色々工夫するのでしょうから、媒介変数が1つの場合の曲線はあんか感じで良いのではないかと思います。しかし、言うまでもないですがこれは曲面ではなく曲線ですよね。確認しておきたいのですが、いろはさんが今解決したい課題は、z=f(x,y) で表される関数をどうやって表示するかということでしょうか?
こちらが勘違いしてもいけないので課題をはっきりさせておきたいのです。
|
|
2016/3/22(Tue) 12:57:58|NO.75030
はい、今の課題はz=f(x,y)の関数をどう出力すればいいかです。
処理が重くなるのが見えてますが、どうにかして曲面を描きたいです。そのためにはどうすればいいかってところです。
|
|
2016/3/22(Tue) 13:16:22|NO.75031
メッシュの様に描くのはどうでしょうか?描画したい領域[x0,x1]×[y0,y1]を決めてx方向,y方向に十分細かく分割します。そうすると格子点が沢山できますから、各格子点上でf(x,y)を計算してz座標を求めます。最後に d3module の命令で格子点同士を宜しく線分で結びます。細かいタイルがたくさん並んだような絵ができるでしょう。細かくすればするほど、面は滑らかに見えます。
ただし、単に線分で結んだだけだと、遠くの面を構成する線分が、よりカメラに近い面を透過して見えてしまいますから遠近感が表現できません。工夫が必要になります。
d3module には空間上の点に対応するウィンドウ上の座標を計算してくれる機能がありますから、カメラから遠いタイルから順番にgsquare命令を使って自前で描画していきます。タイル同士の区切りがわかるように、タイルの周りはline命令で縁取っておく必要があります。タイルの角度とカメラの位置に応じてタイルの色を変えるとさらに見やすくなります。光源は+z方向の遙か彼方からxy平面を照らす平行光線で十分だと思います。この場合もちろん、光がタイルに反射してどのように見えるかを計算してやる必要があります。
|
|
2016/3/22(Tue) 13:22:19|NO.75032
いい忘れましたが、カメラの角度が変わってもf(x,y)の再計算をせずに済むよう、計算結果は配列に保存しておくのがよいでしょう。
倍率が変われば見える範囲も変わりますから、描画する領域も広げたり縮めたりしなければいけませんし、細かさも変更しなくてはなりません。(例えばズームアップしたなら分割数を増やさないと具合が悪い。)その時には再計算しなくてはなりません。
|
|
2016/3/22(Tue) 14:00:01|NO.75033
一番簡素なやりかたで作ってみました。ワイヤーフレームです。参考になればいいですが。
#include "d3m.hsp"
#include "hspmath.as"
#define pi 3.1415926535 // 円周率
#define e 2.7182818284 // ネイピア数
#define UnitLengthToPx 5.0 // 1=5px
#define R_CAM 300.0 // カメラと原点との距離
#define WIDTH_GRAPHAREA 800 // グラフエリアの横幅
#define HEIGHT_GRAPHAREA 600
*init // 初期化
screen 0,WIDTH_GRAPHAREA,HEIGHT_GRAPHAREA,4
theta_cam = PI/3.0
phi_cam = PI/3.0
flg_needRedraw = 1 // フラグ「グラフエリア再描画の必要有り」
flg_draggingGraphAreaToMoveCam = 0 // フラグ「カメラ移動の為にグラフエリアをドラッグ中」
x_cam = 0.0 : y_cam = 0.0 : z_cam = 0.0
d3vrotate x_cam, y_cam, z_cam, 0, 0, R_CAM, 0, 1, 0, phi_cam
d3vrotate x_cam, y_cam, z_cam, x_cam, y_cam, z_cam, 0, 0, 1, theta_cam
va = 1.0 // 視野角スケール
d3setcam x_cam*va, y_cam*va, z_cam*va
/* 曲面のzデータ */
#define X_MIN_DRAW_FUNC -10.0 // 描画するxの最小値 (※本当は倍率に応じてこれを変更せねばならない)
#define X_MAX_DRAW_FUNC 10.0
#define Y_MIN_DRAW_FUNC -10.0
#define Y_MAX_DRAW_FUNC 10.0
#define DX_DRAW_FUNC 0.5 // 描画する時のx方向区切り幅 (※本当は倍率に応じてこれを変更せねばならない)
#define DY_DRAW_FUNC 0.5
num_tile_x =(X_MAX_DRAW_FUNC-X_MIN_DRAW_FUNC)/DX_DRAW_FUNC : num_tile_y = (Y_MAX_DRAW_FUNC-Y_MIN_DRAW_FUNC)/DY_DRAW_FUNC
ddim zData, num_tile_x+1, num_tile_y+1
x = X_MIN_DRAW_FUNC
repeat num_tile_x+1
cnt_x = cnt
y = Y_MIN_DRAW_FUNC
repeat num_tile_y+1
#define cnt_y cnt
zData(cnt_x,cnt_y) = mathFunc1(x,y)
#undef cnt_y
y += DY_DRAW_FUNC
loop
x += DX_DRAW_FUNC
loop
/* misc */
leftClick = 0
mx_drag_start = 0 : my_drag_start = 0
phi_cam_drag_start = 0.0
theta_cam_drag_start = 0.0
*main // メインループ
#define t_mainLoop 50 // [ms]メインループ周期
repeat
gosub *moveCam // カメラ移動
if flg_needRedraw { // 再描画の必要があれば
title strf( "θ:%f / ", theta_cam ) + strf( "φ:%f", phi_cam )
gosub *redrawGraphArea
flg_needRedraw = 0 // フラグ回収
}
gosub *checkMouseCursorPosition // マウスカーソル位置監視
await t_mainLoop
loop
*checkMouseCursorPosition // マウスカーソル位置監視
mx_lastCheck = mousex : my_lastCheck = mousey
return
*redrawGraphArea // グラフエリアの再描画
logmes "Redrew graph area."
redraw 0
color : boxf
gosub *draw_axis // 軸の描画
gosub *draw_surface // 曲面の描画
redraw 1
return
*draw_axis //軸の描画
#define k UnitLengthToPx //名前が長ったらしいので局所的に別名を使う
color 0,255,0
d3mes "X", k*22, 0, 0
d3mes "Y", 0, k*22, 0
d3mes "Z", 0, 0, k*22
color 127,255,255 //5pxで「1」
d3mes "10", k*10, 0, 0:d3mes "-10", -k*10, 0, 0
d3mes "10", 0, k*10, 0:d3mes "-10", 0, -k*10, 0
d3mes "10", 0, 0, k*10:d3mes "-10", 0, 0, -k*10
d3mes "50", k*50, 0, 0:d3mes "-50", -k*50, 0, 0
d3mes "50", 0, k*50, 0:d3mes "-50", 0, -k*50, 0
d3mes "50", 0, 0, k*50:d3mes "-50", 0, 0, -k*50
color 0,255,0
d3arrow k*200, 0, 0, -k*200, 0, 0
d3arrow 0, k*200, 0, 0, -k*200, 0
d3arrow 0, 0, k*200, 0, 0, -k*200
#undef k // 片付け
return
*draw_surface // 関数の張る曲面を描画
#define k UnitLengthToPx
redraw 0
color 0,255,255
repnum_x = (X_MAX_DRAW_FUNC-X_MIN_DRAW_FUNC)/DX_DRAW_FUNC // x方向の繰り返し回数
repnum_y = (Y_MAX_DRAW_FUNC-Y_MIN_DRAW_FUNC)/DY_DRAW_FUNC
x = X_MIN_DRAW_FUNC
repeat repnum_x // x方向に走査
cnt_x = cnt
y = Y_MIN_DRAW_FUNC
repeat repnum_y // y方向に走査
#define cnt_y cnt
//d3line x, y, zData(cnt_x,cnt_y), x+DX_DRAW_FUNC, y, zData(cnt_x+1,cnt_y)
//d3line x, y, zData(cnt_x,cnt_y), x, y+DY_DRAW_FUNC, zData(cnt_x,cnt_y+1)
d3line k*x, k*y, k*zData(cnt_x,cnt_y), k*(x+DX_DRAW_FUNC), k*y, k*zData(cnt_x+1,cnt_y)
d3line k*x, k*y, k*zData(cnt_x,cnt_y), k*x, k*(y+DY_DRAW_FUNC), k*zData(cnt_x,cnt_y+1)
y += DY_DRAW_FUNC
#undef cnt_y
loop
x += DX_DRAW_FUNC
loop
redraw 1
#undef k
return
*moveCam // カメラ移動
/* 視点移動 */
getkey leftClick, 1
if leftClick {
if flg_draggingGraphAreaToMoveCam { // ドラッグ中なら
if ((mousex!=mx_lastCheck)|(mousey!=my_lastCheck)) { // カーソルが動いたなら
/* カメラ移動 */
theta_cam = theta_cam_drag_start + PI/HEIGHT_GRAPHAREA*(my_drag_start - my_lastCheck)
phi_cam = limitf(phi_cam_drag_start + PI/WIDTH_GRAPHAREA*(mx_drag_start - mx_lastCheck), 0.01, PI-0.01)
d3vrotate x_cam, y_cam, z_cam, 0, 0, R_CAM, 0, 1, 0, theta_cam
d3vrotate x_cam, y_cam, z_cam, x_cam, y_cam, z_cam, 0, 0, 1, phi_cam
d3setcam x_cam*va, y_cam*va, z_cam*va
flg_needRedraw = 1 // 再描画の必要有り
}
} else { // ドラッグ中でないなら
logmes "Drag started."
/* ドラッグ開始 */
flg_draggingGraphAreaToMoveCam = 1
/* ドラッグ開始ポイントを記録 */
theta_cam_drag_start = theta_cam
phi_cam_drag_start = phi_cam
mx_drag_start = mx_lastCheck : my_drag_start = my_lastCheck
}
} else {
if flg_draggingGraphAreaToMoveCam == 1 : flg_draggingGraphAreaToMoveCam = 0 : logmes "Drag ended." // ドラッグ解除
}
/* 倍率変更 */
wheel = mousew
flg = 0
if (va < 65.0)&(wheel < 0) : va += 0.009 : flg = 1
if (va > 1.0)&(wheel > 0) : va -= 0.009 : flg = 1
if (va <= 1.0)&(va > 0.001)&(wheel > 0) : va -= 0.009 : flg = 1
if (va <= 1.0)&(va > 0.001)&(wheel < 0) : va += 0.009 : flg = 1
if flg {
d3setcam x_cam*va, y_cam*va, z_cam*va
flg_needRedraw = 1 // 再描画の必要有り
}
return
#module mathFuncs // 関数サンプル
#define e e@ // グローバル変数をモジュール内で参照するための定義
#defcfunc mathFunc1 double x, double y
r = sqrt(x*x + y*y)
return 10.0*cos(r)*powf(e,-0.3*r)
#global
いろはさんのオリジナルのコードでは50ms毎に必ず再描画していましたが、カメラが動いた時のみ再描画するように変更しました。CPU負荷がぐんと下がるはずです。
| |
|
2016/3/23(Wed) 00:59:21|NO.75041
ワイヤーフレームでやるというのはこちらもとてもいいと思いました。関数の変化が目ですぐわかるのでとてもいいです。
とりあえず陽関数の曲面は出力することに成功しました。ですが陽関数のみでは複雑な曲面出力が出来ず媒介変数を2つ使う方法を考えようという結論に至ったのですが、私の高校生にちょっと毛が生えた程度の数学力ではどう命令すればmotchyさんのスクリプトを2つの媒介変数に対応させればよいのかわかりません。
そうして対応させたら自分のスクリプトに対応させるようにプログラムをしてみようと思っています。どなたかご教授お願いできないでしょうか。それともそもそも2つの媒介変数を用いることは無理な話なのでしょうか。
とりあえず可能かの有無が知りたいです。できればアドバイス等も。
|
|
2016/3/23(Wed) 09:30:44|NO.75047
すみません、ミスったので再投稿です。
前回のスクリプトを改良してみました。ズームアップ/ダウンに合わせて描画範囲を変更し、隠面処理も施してみました。座標軸と関数の曲面をどう上手く同時に表示するかという課題は残っていますが。
これはプログラム面でのアドバイスであって、数学的には前回から何も進歩していません。
#include "d3m.hsp"
#define pi 3.1415926535 // 円周率
#define e 2.7182818284 // ネイピア数
#define UnitLengthToPx 5.0 // 1=5px
#define R_CAM 300.0 // カメラと原点との距離
#define WIDTH_GRAPHAREA 800 // グラフエリアの横幅
#define HEIGHT_GRAPHAREA 600
*init // 初期化
screen 0,WIDTH_GRAPHAREA,HEIGHT_GRAPHAREA,4
/* misc */
zData = 0
leftClick = 0
mx_drag_start = 0 : my_drag_start = 0
phi_cam_drag_start = 0.0
theta_cam_drag_start = 0.0
/* カメラ */
theta_cam = PI/3.0
phi_cam = PI/3.0
va = 0.5 // 視野角スケール
x_cam = 0.0 : y_cam = 0.0 : z_cam = 0.0
d3vrotate x_cam, y_cam, z_cam, 0, 0, R_CAM, 0, 1, 0, phi_cam
d3vrotate x_cam, y_cam, z_cam, x_cam, y_cam, z_cam, 0, 0, 1, theta_cam
d3setcam x_cam*va, y_cam*va, z_cam*va
/* 曲面のzデータ */
/* zDataCondition */
x_min_draw_func = 0.0 : x_max_draw_func = 0.0 : y_min_draw_func = 0.0 : y_max_draw_func = 0.0 // 描画する矩形領域の境界情報
dx_draw_func = 0.0 : dy_draw_func = 0.0 // 微小面素のサイズ
num_div_x_draw_func = 0 : num_div_y_draw_func = 0 // x,y方向分割数
/* flags */
flg_needUpdate_zData = 1 // zデータ更新の必要有り
flg_needRedraw = 1 // グラフエリア再描画の必要有り
flg_draggingGraphAreaTooperateCam = 0 // カメラ移動の為にグラフエリアをドラッグ中
*main // メインループ
#define t_mainLoop 50 // [ms]メインループ周期
repeat
gosub *operateCam // カメラ移動
if flg_needUpdate_zData { // zデータ更新の必要があるならば
calc_zDataCondition x_min_draw_func, x_max_draw_func, y_min_draw_func, y_max_draw_func, num_div_x_draw_func, num_div_y_draw_func // zDataConditionを計算
create_zData zData, x_min_draw_func, x_max_draw_func, y_min_draw_func, y_max_draw_func, num_div_x_draw_func, num_div_y_draw_func // zデータを生成
dx_draw_func = (x_max_draw_func-x_min_draw_func)/num_div_x_draw_func : dy_draw_func = (y_max_draw_func-y_min_draw_func)/num_div_y_draw_func
flg_needUpdate_zData = 0 // フラグ回収
logmes "Updated zData."
}
if flg_needRedraw { // 再描画の必要があれば
title strf( "θ:%f / ", theta_cam ) + strf( "φ:%f", phi_cam )
gosub *redrawGraphArea
flg_needRedraw = 0 // フラグ回収
logmes "Redrew graph area."
}
gosub *checkMouseCursorPosition // マウスカーソル位置監視
await t_mainLoop
loop
*checkMouseCursorPosition // マウスカーソル位置監視
mx_lastCheck = mousex : my_lastCheck = mousey
return
*redrawGraphArea // グラフエリアの再描画
redraw 0
color : boxf
gosub *draw_axis // 軸の描画
gosub *draw_surface // 曲面の描画
redraw 1
return
*draw_axis //軸の描画
#define k UnitLengthToPx //名前が長くて面倒なので局所的に別名を使う
color 0,255,0
d3mes "X", k*22, 0, 0
d3mes "Y", 0, k*22, 0
d3mes "Z", 0, 0, k*22
color 127,255,255 //5pxで「1」
d3mes "10", k*10, 0, 0:d3mes "-10", -k*10, 0, 0
d3mes "10", 0, k*10, 0:d3mes "-10", 0, -k*10, 0
d3mes "10", 0, 0, k*10:d3mes "-10", 0, 0, -k*10
d3mes "50", k*50, 0, 0:d3mes "-50", -k*50, 0, 0
d3mes "50", 0, k*50, 0:d3mes "-50", 0, -k*50, 0
d3mes "50", 0, 0, k*50:d3mes "-50", 0, 0, -k*50
color 0,255,0
d3arrow k*200, 0, 0, -k*200, 0, 0
d3arrow 0, k*200, 0, 0, -k*200, 0
d3arrow 0, 0, k*200, 0, 0, -k*200
#undef k // 後片付け
return
*draw_surface // 関数の張る曲面を描画
#define xmin x_min_draw_func //名前が長くて面倒なので局所的に別名を使う
#define xmax x_max_draw_func
#define ymin y_min_draw_func
#define ymax y_max_draw_func
#define nx num_div_x_draw_func
#define ny num_div_y_draw_func
#define dx dx_draw_func
#define dy dy_draw_func
gmode 3,1,1, 200 //少し透かしてみる
color 0,255,255
redraw 0
repeat 1 // タイルの描画順は phi_cam の範囲によって4パターンある
if (0.25*PI <= phi_cam)&(phi_cam < 0.75*PI) {
repeat ny
cnty = cnt
repeat nx
#define cntx cnt
drawFuncSurfaceTile zData, cntx,cnty
#undef cntx
loop
loop
break
}
if (0.75*PI <= phi_cam)&(phi_cam < 1.25*PI) {
repeat nx
cntx = cnt
repeat ny
#define cnty cnt
drawFuncSurfaceTile zData, nx-1-cntx, cnty
#undef cnty
loop
loop
break
}
if (1.25*PI <= phi_cam)&(phi_cam < 1.75*PI) {
repeat ny
cnty = cnt
repeat nx
#define cntx cnt
drawFuncSurfaceTile zData, nx-1-cntx, ny-1-cnty
#undef cntx
loop
loop
break
}
if ((0.0 <= phi_cam)&(phi_cam < 0.25*PI))|((1.75*PI <= phi_cam)&(phi_cam < 2.0*PI)) {
repeat nx
cntx = cnt
repeat ny
#define cnty cnt
drawFuncSurfaceTile zData, cntx, ny-1-cnty
#undef cnty
loop
loop
break
}
loop
redraw 1
#undef xmin // 後片付け
#undef xmax
#undef ymin
#undef ymax
#undef nx
#undef ny
return
*operateCam // カメラ操作
/* 視点移動 */
getkey leftClick, 1
if leftClick {
if flg_draggingGraphAreaTooperateCam { // ドラッグ中なら
if ((mousex!=mx_lastCheck)|(mousey!=my_lastCheck)) { // カーソルが動いたなら
/* カメラ移動 */
theta_cam = limitf(theta_cam_drag_start + PI/HEIGHT_GRAPHAREA*(my_drag_start - my_lastCheck), 0.01, PI-0.01)
phi_cam = limitf(phi_cam_drag_start + PI/WIDTH_GRAPHAREA*(mx_drag_start - mx_lastCheck), 0.01, 2.0*PI-0.01)
d3vrotate x_cam, y_cam, z_cam, 0, 0, R_CAM, 0, 1, 0, theta_cam
d3vrotate x_cam, y_cam, z_cam, x_cam, y_cam, z_cam, 0, 0, 1, phi_cam
d3setcam x_cam*va, y_cam*va, z_cam*va
flg_needRedraw = 1 // 再描画の必要有り
}
} else { // ドラッグ中でないなら
logmes "Drag started."
/* ドラッグ開始 */
flg_draggingGraphAreaToOperateCam = 1
/* ドラッグ開始ポイントを記録 */
theta_cam_drag_start = theta_cam
phi_cam_drag_start = phi_cam
mx_drag_start = mx_lastCheck : my_drag_start = my_lastCheck
}
} else {
if flg_draggingGraphAreaToOperateCam == 1 : flg_draggingGraphAreaToOperateCam = 0 : logmes "Drag ended." // ドラッグ解除
}
/* 倍率変更 */
wheel = mousew
flg = 0
if (va < 65.0)&(wheel < 0) : va += 0.1*va : flg = 1
if (va > 1.0)&(wheel > 0) : va -= 0.1*va : flg = 1
if (va <= 1.0)&(va > 0.001)&(wheel > 0) : va -= 0.1*va : flg = 1
if (va <= 1.0)&(va > 0.001)&(wheel < 0) : va += 0.1*va : flg = 1
if flg {
d3setcam x_cam*va, y_cam*va, z_cam*va
flg_needUpdate_zData = 1 // zデータ更新の必要有り
flg_needRedraw = 1 // 再描画の必要有り
}
return
#module mathFuncs // 関数サンプル
#define e e@ // グローバル変数/定数をモジュール内で参照するための定義
#defcfunc mathFunc1 double x, double y
r = sqrt(x*x + y*y)
return 10.0*cos(r)*powf(e,-0.3*r)
#global
#module module1
#define va va@ // グローバル変数/定数をモジュール内で参照するための定義
#deffunc calc_zDataCondition var xmin,var xmax,var ymin,var ymax, var nx,var ny // zDataConditionを計算
/*
xmax : x_min_draw_func
nx : num_div_x_draw_func
*/
nx = 50 : ny = 50 // ※本当はユーザーがこれを変更できるようにせねばならない。
xmin = -va*50.0 : xmax = -xmin : ymin = -va*50.0 : ymax = -ymin
return
#global
#module module2
#deffunc create_zData array zData, double xmin,double xmax,double ymin,double ymax, int nx,int ny // zデータの生成
/*
xmax : x_min_draw_func
nx : num_div_x_draw_func
タイルのインデックスの基点は左下(xmin,ymin)とする
*/
dx = (xmax-xmin)/nx : dy = (ymax-ymin)/ny // 微小面素のサイズ
ddim zData, nx+1,ny+1, 2
#enum IDX_Z = 0 // 配列のインデックス
#enum IDX_COSINE
/* z値の書き込み */
x = xmin
repeat nx+1
cnt_x = cnt
y = ymin
repeat ny+1
#define cnt_y cnt
zData(cnt_x, cnt_y, IDX_Z) = mathFunc1(x,y)
y += dy
#undef cnt_y
loop
x += dx
loop
/* 微小面素の上向き法線ベクトルとz方向単位ベクトルとのなす角の余弦を計算 */
repeat nx
cnt_x = cnt
repeat ny
#define cnt_y cnt
z1 = zData(cnt_x+1,cnt_y,IDX_Z) - zData(cnt_x,cnt_y,IDX_Z) : z2 = zData(cnt_x,cnt_y+1,IDX_Z) - zData(cnt_x,cnt_y,IDX_Z)
xv = -z1*dy : yv = -dx*z2 : zv = dx*dy // 法線ベクトル
zData(cnt_x,cnt_y,IDX_COSINE) = zv/sqrt(xv*xv + yv*yv + zv*zv) //余弦
#undef cnt_y
loop
loop
return
#global
#module module3
#define k UnitLengthToPx@
#define xmin x_min_draw_func@
#define xmax x_max_draw_func@
#define ymin y_min_draw_func@
#define ymax y_max_draw_func@
#define dx dx_draw_func@
#define dy dy_draw_func@
#enum IDX_Z = 0 // 配列のインデックス
#enum IDX_COSINE
dst_x_gsquare = 0 : dst_y_gsquare = 0 //未初期化変数警告回避
#deffunc drawFuncSurfaceTile array zData, int idx_x, int idx_y
#define x1 xmin + dx*idx_x
#define y1 ymin + dy*idx_y
#define z1 zData(idx_x,idx_y,IDX_Z)
#define x2 x1 + dx
#define y2 y1
#define z2 zData(idx_x+1,idx_y,IDX_Z)
#define x3 x2
#define y3 y2+dy
#define z3 zData(idx_x+1,idx_y+1,IDX_Z)
#define x4 x1
#define y4 y3
#define z4 zData(idx_x,idx_y+1,IDX_Z)
brightness = absf(zData(idx_x,idx_y,IDX_COSINE))
vertex_tile = x1,y1,z1, x2,y2,z2, x3,y3,z3, x4,y4,z4
flg_error = 0
repeat 4
d3getpos x_graph,y_graph, k*vertex_tile(cnt*3),k*vertex_tile(cnt*3+1),k*vertex_tile(cnt*3+2)
if stat == 0 : flg_error = 1 : break //座標変換に失敗
dst_x_gsquare(cnt) = x_graph : dst_y_gsquare(cnt) = y_graph
loop
if flg_error : return
color limitf(brightness*255,50,220),0,0
gsquare -1, dst_x_gsquare, dst_y_gsquare
/* 縁取り */
color 255,0,0
repeat 3
line dst_x_gsquare(cnt),dst_y_gsquare(cnt), dst_x_gsquare(cnt+1),dst_y_gsquare(cnt+1)
loop
return
#global
上のスクリプトは参考として置いておくとして、いろはさんは次の課題に挑まれるわけですね。媒介変数を2つ使うというのがピンと来ないのですが、もしかして x=f(u,v), y=g(u,v), z=h(x,y) のようなものですか?それとも、また別のものですか?具体例(数式)をひとつ挙げてもらえると助かります。
| |
|
2016/3/23(Wed) 09:42:40|NO.75048
※↑のスクリプトではわざとタイルを少し透過させていますが、105行目 gmode 3,1,1, 200 の 200を255に変えることで全く透けなくなります。こういった数学のグラフに限れば面素群をzソートしなくてもいいので高速に描画できます。
|
|
2016/3/23(Wed) 13:33:40|NO.75049
x=f(u,v),y=g(u,v),z=h(u,v)といった形式のものです。
1つ例を挙げるとならば、x=rcos(s)cos(t),y=rcos(s)sin(t),z=rsin(s)といった球体等です。
|
|
2016/3/23(Wed) 14:57:52|NO.75050
なるほど、そういう類の曲面ですか。複雑な図形も表現できますね。(※球ですと私の周りでは x=r*sin(θ)*cos(φ), y=r*sin(θ)*sin(φ), z=r*cos(θ) の方を良く目にします(本質的には一緒)。電磁理論の教科書なんかでは専らこちら。)
結論から言うと、描けます。実は私も嘗てそんなことをHSPでやっていた者です。言葉で説明すると結構わかりにくいのですが、まぁやってみましょうか。私の馴染みのあるタイプの球で説明しましょう。
2つの独立変数(θ,φ)があるのなら、当然、定義域があるわけです。θ∈[-π,π], φ∈[0,2π]。同時に動かすのは無茶ですから片方θを固定しておいて他方φを動かします。φが一回りしたらθを微小に動かしてまた固定。φを一回りさせます。これをどんどん繰り返してθの定義域を舐めつくしたら終了です。(※θ,φどちらを固定するかは人間の都合なので入れ替えてもOK) それで先程のz=f(x,y)と同じ要領でメッシュを張れば良いのです。
日本語だとわかりにくいですね。ソースコードで表すなら、
min_theta = -PI : max_theta = PI //θの定義域
min_phi = 0 : max_phi = 2.0*PI //φの定義域
num_div_theta = 100 //θの分割数
num_div_phi = 100 //φの分割数
d_theta = (max_theta-min_theta)/num_div_theta //dθ
d_phi = (max_phi-min_phi)/num_div_phi //dφ
ddim vertexData, num_div_theta, num_div_phi, 3 //頂点データ用の配列
#enum IDX_X_vertexData = 0
#enum IDX_Y_vertexData
#unum IDX_Z_vertexData
theta = min_theta
repeat num_div_theta
cnt_theta = cnt
phi = min_phi
repeat num_div_phi
#define cnt_phi cnt
vertexData(cnt_theta,cnt_phi,IDX_X_vertexData) = r*sin(theta)*cos(phi)
vertexData(cnt_theta,cnt_phi,IDX_Y_vertexData) = r*sin(theta)*sin(phi)
vertexData(cnt_theta,cnt_phi,IDX_Z_vertexData) = r*cos(theta)
phi += d_phi
loop
theta += d_theta
loop
こうやって用意した頂点データを上手く線分で結ぶのが少し難しいですが、大抵は安直なやりかたでそこそこの見栄えにできます。
(theta,phi)に対応する頂点を(theta,phi+d_phi)に対応する頂点を線分で結びます。それから、(theta,phi)に対応する頂点と(theta+d_theta,phi)に対応する頂点を線分で結びます。これを終わりまで繰り返せばいい感じにメッシュを張れます。ただし定義域の端には気をつけてください。theta+d_thetaが定義域をオーバーランしますので、そこは諦めるのが一番簡単です。布地の端っこみたいですね。ほつれそうな感じの。
| |
|
2016/3/23(Wed) 15:31:39|NO.75051
ただし、これくらい複雑になってくると色々と面倒なことが起こり始めます。上の球なんかではワイヤーフレームで描いてもまぁいいのですが、奥の面と手前の面が一緒くたに描画されますから少々わかりにくくなります。そこで隠面処理を行おうとすると、微小面素をカメラからの距離に応じて遠い順に並べ替えて(zソート)、奥のものから順番に描いていかねばなりません。(画家のアルゴリズム)
分割を細かくすれば微小面素の枚数はあっという間に数千,数万に達します。これだけのものをzソートしようとすると膨大な計算量になりますから処理時間が長くなります。マウスでグリグリ気持よく回すことは期待できなくなります。ガクガクです。
実感がわかないと思うのでこれを見てください。私が昔作ったものです。
http://hsproom.me/program/view/?p=23
分割数を増やすと急激に遅くなるのがわかると思います。(※50x50でポリゴンが欠け始めるのはHSP部屋の特性(設定次第で修正できるものですが)なので気にしないでください。)
少しでも面素の枚数を減らそうと思えば、カメラがズームアップした時に、分割数を増やすと同時に、見えている範囲のみに計算を絞る必要があります。これが恐ろしく大変です。見えている範囲に対応する(θ,φ)の領域を算出しなければなりませんから。しかもその領域は(θ,φ)空間の矩形領域にはまずなりません。θとφの陰関数で表現される非常に厄介な領域になるでしょう。こんな領域をどうやって上手く舐めつくすか考えるのは頭が痛いことです。この辺りにくると工学系の私は非常に苦しくなってきます(笑)。要するに、メッシュのきめ細かさをそう簡単には変更できないことになります。多峰性を有する曲面を表現するにはきめ細かいメッシュが必要なのに、そうもいかないということです。
そこでですが、HSPの標準命令に頼る3D描画をやめて(=d3moduleから離れて)、プラグインもしくは拡張ランタイムを使う高速描画でゴリ押すというのも1つの手かと思います。なんだか最近はHGIMG4なるランタイムが熱いみたいですよ。頂点と微小面素の計算は標準命令で間に合います。算出したポリゴンをHGIMG4等に叩き込んで撮影させるのはどうでしょう? 尤も、私は使ったことがないのでそういうことが可能なのかどうかはわかりませんが。多分ポリゴンの登録機能くらいは付いているんじゃないでしょうか?
| |
|
2016/3/24(Thu) 00:47:31|NO.75052
ありがとうございます。motchyさんの提案も少し検討してみます。こちらのほうが自分にあっていれば乗り換えてみようかと。
とりあえず、ある程度曲面は出力することが出来たので今回は一応これで解決にします。
他に何かいい案や何かこうした法がいいとかがありましたら続きき書いていってもらえると嬉しいです。
是非今後の開発の参考にさせていただきます。
|
|