SDL2 で塗りつぶした楕円の回転

 塗りつぶした楕円は、回転させなければ、円の塗りつぶしと同様に描くことができます。

void fill_oval(SDL_Renderer *renderer, int sx, int sy, int a, int b, int angle) {
    SDL_Point p1, p2;
    int x, y;

    for( x = -a; x <= a; x++ ) {
        y = sqrt(1.0 - (double)(x*x)/(a*a))*b +0.5;
        p1.x = x, p1.y = y;
        rotate(&p1, angle);
        p2.x = x, p2.y = -y;
        rotate(&p2, angle);
        SDL_RenderDrawLine(renderer, sx + p1.x, sy - p1.y, sx + p2.x, sy - p2.y);
    }
}
塗りつぶした楕円

 ところが、回転させると、うまく塗りつぶせません。

塗りつぶした楕円を、45 度回転させる

 別な考え方で塗りつぶします。楕円の方程式は以下です。

楕円の方程式

 ある点が、この楕円の中にあるかどうかを判断し、楕円の中にあれば点を描画し、無ければ描画しない。という手順で塗りつぶしを行います。(x, y) が楕円内にあるかどうかは、次の式で判断できます。

楕円内かどうかの条件

 上記の式をちょっと変形しておきます。

楕円内かどうかの条件

 これを使い、楕円を塗りつぶしてみます。

void fill_oval(SDL_Renderer *renderer, int cx, int cy, int a, int b) 
{
    int x, y, result, out;

    out = a*a * b*b;

    for( x = -a; x <= a; x++ ) {
        for( y = -b; y <= b; y++ ) {
            result = (x * x)*(b * b) + (y * y)*(a * a);
            if( result <= out ) {
                SDL_RenderDrawPoint(renderer, cx + x, cy - y);
            }
        }
    }
}
塗りつぶした楕円

 これを回転出来るようにします。考え方としては、楕円を含む矩形を回転させ、調べる領域を求め、各点を調べるときに、逆に回転させ、調べます。

void rotate(SDL_Point *pt, int angle) {
    int x, y;
    double r;

    x = pt->x, y = pt->y;
    r = M_PI * angle / 180.;

    pt->x = x * cos(r) - y * sin(r);
    pt->y = x * sin(r) + y * cos(r);
}

void min_max(SDL_Point *p, SDL_Point *min, SDL_Point *max, int angle) {
    rotate(p, angle);
    min->x = min->x > p->x ? p->x : min->x;
    min->y = min->y > p->y ? p->y : min->y;
    max->x = max->x < p->x ? p->x : max->x;
    max->y = max->y < p->y ? p->y : max->y;
}

void fill_oval(SDL_Renderer *renderer, int cx, int cy, int a, int b, int angle) {
    int x, y, result, out;
    SDL_Point p, min, max;

    /* 回転後の描画範囲を求める */
    min.x = max.x = 0;
    min.y = max.y = 0;
    p.x = -a, p.y = -b;
    min_max(&p, &min, &max, angle);
    p.x = -a, p.y = b;
    min_max(&p, &min, &max, angle);
    p.x = a, p.y = b;
    min_max(&p, &min, &max, angle);
    p.x = a, p.y = -b;
    min_max(&p, &min, &max, angle);

    out = a*a * b*b;
    for( x = min.x; x <= max.x; x++ ) {
        for( y = min.y; y <= max.y; y++ ) {
            p.x = x, p.y = y;
            rotate(&p, -angle);
            result = (p.x * p.x)*(b * b) + (p.y * p.y)*(a * a);
            if( result <= out ) {
                SDL_RenderDrawPoint(renderer, cx + x, cy - y);
            }
        }
    }
}
塗りつぶした楕円を、45 度回転させる

 これで塗りつぶしがおかしくなることはなくなりました。塗りつぶしの時は、この方法を使ったほうがいいようです。

SDL2

Posted by sirius