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;
}

次は、楕円を書いてみたいと思います。

ディスカッション
コメント一覧
まだ、コメントがありません