SDL2 で円を描画する
SDL2 の描画は、点、線、矩形の描画はできますが、円は描画ができません。円を描画する関数を作成してみます。
円は、円の座標を計算して、そこに点を描画して表現します。円の方程式は以下です。

r は半径です。以下のような円です。

これは、原点を中心とした円の方程式です。上の式を変形して、x から y を求めます。

上の式から y を求めれば、xy 座標が決まるのでその位置に点を描きます。ただし、計算で求めた座標は、原点を中心とした直交座標での座標なので、これをスクリーン座標に変えなければなりません。
まず、円の方程式から、直交座標で点を打つ座標を求めます。この場合、x 座標は、-r 〜 r まで変化することになります。
for( x = -r; x <= r; x++ ) { y = sqrt(r * r + x * x) + 0.5; }
これを、スクリーン座標の (sx, sy) を中心に描きます。
for( x = -r; x <= r; x++ ) { y = sqrt(r * r + x * x) + 0.5; px = sx + x; py = sy - y; SDL_RenderDrawPoint(renderer, px, py); }
結果は次のような上半分の円になります。

下側は、-y になるので、それも描きます。
for( x = -r; x <= r; x++ ) { y = sqrt(r * r + x * x) + 0.5; px = sx + x; py = sy - y; SDL_RenderDrawPoint(renderer, px, py); py = sy + y; SDL_RenderDrawPoint(renderer, px, py); }
結果は次のようになります。

円のようになりましたが、左右の部分がつながっていません。これは、x が 1 変化する際に左右に近い部分では、y が 1 より大きく変化しているからです。この場合は、y を基準に計算すれば良いことになります。
/* x を基準に点を描画 */ for( x = -r; x <= r; x++ ) { y = sqrt(r * r + x * x) + 0.5; px = sx + x; py = sy - y; SDL_RenderDrawPoint(renderer, px, py); py = sy + y; SDL_RenderDrawPoint(renderer, px, py); } /* y を基準に点を描画 */ for( y = -r; y <= r; y++ ) { x = sqrt(r * r + y * y) + 0.5; px = sx + x; py = sy - y; SDL_RenderDrawPoint(renderer, px, py); px = sx - y; SDL_RenderDrawPoint(renderer, px, py); }
これで次のようなきれいな円になります。

塗りつぶした円は、x 座標を基準に、-y 〜 y まで直線を引けば塗りつぶされます。
for( x = -r; x <= r; x++ ) { y = sqrt(r * r + x * x) + 0.5; px = sx + x; py1 = sy - y; py2 = sy + y; SDL_RenderDrawLine(renderer, px, py1, px, py2); }

これらを関数にまとめます。
void draw_circle(SDL_Renderer *renderer, int sx, int sy, int r) { int x, y, px, py; /* x を基準に点を描画 */ for( x = -r; x <= r; x++ ) { y = sqrt(r*r - x*x) + 0.5; px = sx + x; py = sy - y; SDL_RenderDrawPoint(renderer, px, py); py = sy + y; SDL_RenderDrawPoint(renderer, px, py); } /* y を基準に点を描画 */ for( y = -r; y <=r; y++ ) { x = sqrt(r*r - y*y) + 0.5; px = sx + x; py = sy - y; SDL_RenderDrawPoint(renderer, px, py); px = sx - x; SDL_RenderDrawPoint(renderer, px, py); } } void fill_circle(SDL_Renderer *renderer, int sx, int sy, int r) { int x, y, px, py1, py2; for( x = -r; x <= r; x++ ) { y = sqrt(r*r - x*x) + 0.5; px = sx + x; py1 = sy - y; py2 = sy + y; SDL_RenderDrawLine(renderer, px, py1, px, py2); } }
これで円を描けるようになったので、アニメーションさせてみます。
#include <stdio.h> #include <SDL.h> void draw_circle(SDL_Renderer *renderer, int sx, int sy, int r) { int x, y, px, py; /* x を基準に点を描画 */ for( x = -r; x <= r; x++ ) { y = sqrt(r*r - x*x) + 0.5; px = sx + x; py = sy - y; SDL_RenderDrawPoint(renderer, px, py); py = sy + y; SDL_RenderDrawPoint(renderer, px, py); } /* y を基準に点を描画 */ for( y = -r; y <=r; y++ ) { x = sqrt(r*r - y*y) + 0.5; px = sx + x; py = sy - y; SDL_RenderDrawPoint(renderer, px, py); px = sx - x; SDL_RenderDrawPoint(renderer, px, py); } } void fill_circle(SDL_Renderer *renderer, int sx, int sy, int r) { int x, y, px, py1, py2; for( x = -r; x <= r; x++ ) { y = sqrt(r*r - x*x) + 0.5; px = sx + x; py1 = sy - y; py2 = sy + y; SDL_RenderDrawLine(renderer, px, py1, px, py2); } } void *draw(void *param) { SDL_Renderer *renderer; int x, y, r; static int d = 0; renderer = (SDL_Renderer *)param; /* 描画 */ SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); SDL_RenderClear(renderer); SDL_SetRenderDrawColor(renderer, 0xff, 0xff, 0xff, SDL_ALPHA_OPAQUE); /* 円描画 */ x = 10 + d; y = 10 + d*3/4; r = 10; draw_circle(renderer, x, y, r); SDL_SetRenderDrawColor(renderer, 0, 0xff, 0xff, SDL_ALPHA_OPAQUE); y = 230 - d*3/4; fill_circle(renderer, x, y, r); d = (d + 10) %310; /* バッファをウィドウに反映 */ SDL_RenderPresent(renderer); } Uint32 draw_timer(Uint32 interval, void *param) { SDL_Event event; SDL_UserEvent userevent; /* コールバックでSDL_USEREVENTイベントをキューに入れる。 このコールバック関数は一定の周期で再び呼ばれる */ userevent.type = SDL_USEREVENT; userevent.code = 0; userevent.data1 = &draw; userevent.data2 = param; event.type = SDL_USEREVENT; event.user = userevent; SDL_PushEvent(&event); return(interval); } int main(int argc, char* argv[]){ SDL_Event event; SDL_Window *window; SDL_Renderer *renderer; SDL_TimerID my_timer_id; int quit_flg = 1; /* 初期化 */ if (SDL_Init(SDL_INIT_VIDEO) < 0) { fprintf(stderr, "SDL_Init(): %s\n", SDL_GetError()); exit(1); } /* ウィンドウ作成 */ window = SDL_CreateWindow("SDL Test", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 320, 240, SDL_WINDOW_OPENGL); if( window == NULL ) { printf("Can not create window\n"); exit(1); } renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); if( renderer == NULL ) { printf("Can not create renderer\n"); exit(1); } /* 強制終了時に SDL_Quit() を呼ぶ */ atexit(SDL_Quit); /* タイマー設定 */ my_timer_id = SDL_AddTimer(200, draw_timer, (void *)renderer); /* イベントループ */ while(quit_flg) { while( SDL_PollEvent(&event) ) { switch (event.type) { /* タイマー処理 */ case SDL_USEREVENT: { void (*p) (void*) = event.user.data1; p(event.user.data2); break; } /* 終了 */ case SDL_KEYDOWN: case SDL_QUIT: quit_flg = 0; break; } } /* イベントがない場合、少し待つ */ SDL_Delay(50); } if( my_timer_id ) SDL_RemoveTimer(my_timer_id); if( renderer ) SDL_DestroyRenderer(renderer); if( window ) SDL_DestroyWindow(window); SDL_Quit(); return 0; }

次は、楕円を書いてみたいと思います。
ディスカッション
コメント一覧
まだ、コメントがありません