C++/SDL2 - 渲染圆

edu*_*unn 1 c++ graphics geometry sdl-2

我是在正确的轨道上,用SDL2绘制一个圆圈吗?我认为使用参数方程和趋于零的半径将起作用,但就处理器使用而言似乎效率非常低.非常感谢任何其他想法提前感谢.

    //Circle test
    int circle_x = WINDOW_WIDTH/2;
    int circle_y = WINDOW_HEIGHT/2;
    int circle_radius = 100;
    SDL_SetRenderDrawColor(window.renderer, 100, 100, 255, 255);

    int point_x;
    int point_y;

    while (circle_radius > 0)
    {
        for (int t = 0; t < 360; t++)
        {
            point_x = circle_x + circle_radius * cos(t);
            point_y = circle_y + circle_radius * sin(t);
            SDL_RenderDrawPoint(window.renderer, point_x, point_y);
        }

        circle_radius--;
    }
Run Code Online (Sandbox Code Playgroud)

输出Img

Lui*_*ado 6

首先,你的代码中存在一些错误,因为sin()cos()函数是弧度而不是基于度数的函数.这将使您的圆圈出现在伪随机选择的点上,因为每个步骤绘制一个与前一个点相差大约57度的点.这将没有影响,因为现代图形工作缓冲,你会看到最终的结果,而不是工作.

一旦说到这一点,今天没有人使用你的暴露算法画一个圆圈.看看Bresenham的中间点算法,它基本上试图用八分圆绘制一个圆,但要快几倍.

这些算法背后的想法是考虑R^2 = x^2 + y^2公式并在其中一个轴上逐个像素地绘制,并考虑何时必须遵循另一个轴(您通过八分圆绘制,因为这样您不应该处理大于一个衍生品,你只需决定你是否向上移动).例程还考虑了圆对称性,仅计算一个八分圆,然后在每次通过时绘制八个点.

当我年轻的时候从头开始开发那个算法(之前没见过Bresenham)我可能对解决方案的推理对你有所帮助.

第一次尝试是考虑到小圆圈的分辨率(粒度不依赖于角度),你必须绘制比大光圈更少的像素,你必须重新设计你所遵循的一度方法以适应更精细或粗略的决议.我的想法是逐个像素地,而不是逐个程度地去,直到你绘制完整的东西.我们将仅绘制第一个八分圆,并将通过图的对称属性绘制其余部分.我们从这(0, -R)一点开始,逐个像素地走,直到我们到达这(sqrt(2)*R, R - sqrt(2)*R)一点.

我们要做的第一件事就是尽量保存我们必须做的所有操作.我们可以保存操作的第一个地方是计算方块...我们将使用R^2 = x^2 + y^2方程式并且在它上面,R仅用于R^2所有时间,因此,假设我们想要绘制一个十个像素的半径圆,我们将方形的东西100(这是10像素半径的平方).

接下来,我们将看到正方形的一个属性,也就是说,它们从一个正方形变为奇数(0 -> 1(delta is 1) -> 4(delta is 3) -> 9(delta is 5) -> 16(delta is 7) ...),所以如果我们可以安排在x中增长1,我们可以轻松地计算x ^ 2,只需添加两个odd变量,然后添加odd到最后的平方数,所以我们将使用两个数字:xx2.我们初始化为0,第一个增长x += 1;,而第二个增长的关系x2 += dx2; dx2 += 2; (我们初始化dx2 = 1;)这使我们允许xx2增长只做总和,而不是任何乘法.

如果有人认为我们将需要y2 = 100 - x2,然后被迫计算y = sqrt(y2)几乎是正确的,但这里的诀窍是能够像x对应物那样向后管理yy2序列.恩,对,y并且y2可以在相反的方向相同,管理xx2但这一次我们要往回走,从(什么?),以减少奇数1其中dy2 -= 2; y2 -= dy2;,最终达到0.为了做到这一点,检查连续两个方块之间的差别恰恰增加了两个数字,因此,举例来说,差异之间13^2 = 169,并14^2 = 19613 + 14 = 27,这是奇数开始与如果我们回到从R = 140y.

使事情变得如此复杂的原因是,这种方式我们只用整数进行加法而不需要进行乘法运算(好的,乘法并不是那么昂贵,但是有一段时间它们是这样的)好吧,我们做乘法到最初的平方半径R,但我们只在开始时做一次计算R^2.

现在的想法是设置原点出发(0, -R),去正确的,逐像素,加入(和modifiying) x,x2sum(我们.减去总结所有的时间),直到我们到达下一个方块y,然后更新所有y轴值(我们做递减Y,我们必须在那一瞬间移动像素时)y,y2,dy2,并绘制像素(或之前绘制它,因为我们在日常做的),直到......什么?(当然,问题是,直到我们在45度点,其中八分完成满足,xy坐标等于)停在那里,因为从这一点上来说,它是可能的一个步骤使得y坐标是非常重要的增加多个像素(导数大于1),这应该使整个算法复杂化(我们无论如何绘制其他对称的八个点,所以我们绘制图形的另一部分)

所以,假设我们有100个半径,并从以下开始:

x=0, x2= 0, dx2= 1, y=10, y2=100, dy2=19, sum=100  *
x=1, x2= 1, dx2= 3, y= 9, y2= 81, dy2=17, sum= 99
x=2, x2= 4, dx2= 5, y= 9, y2= 81, dy2=17, sum= 96
x=3, x2= 9, dx2= 7, y= 9, y2= 81, dy2=17, sum= 91
x=4, x2=16, dx2= 9, y= 9, y2= 81, dy2=17, sum= 84  *
x=5, x2=25, dx2=11, y= 8, y2= 64, dy2=15, sum= 75  *
x=6, x2=36, dx2=13, y= 7, y2= 49, dy2=13, sum= 64  *
x=7, x2=49, dx2=15, y= 7, y2= 49, dy2=13, sum= 51
Run Code Online (Sandbox Code Playgroud)

标有星号的点是sum值与下一个值相交的点y2,使得y值被去除并且必须移动我们绘制的像素.最后的例程是:

int bh_onlyonepoint(int r, int cx, int cy)
{
    int r2 = r*r;
    int x = 0, x2 = 0, dx2 = 1;
    int y = r, y2 = y*y, dy2 = 2*y - 1;
    int sum = r2;

    while(x <= y) {
            draw(cx + x, cy + y); /* draw the point, see below */
            sum -= dx2;
            x2 += dx2;
            x++;
            dx2 += 2;
            if (sum <= y2) {
                    y--; y2 -= dy2; dy2 -= 2;
            }
    } /* while */
    return x; /* number of points drawn */
}
Run Code Online (Sandbox Code Playgroud)

如果你愿意,我写了一个简单的例子来在屏幕上的ascii中绘制一个圆圈,给定半径作为命令参数.在绘制单个星号之前,它使用ANSI转义序列将光标定位在屏幕中.在X方向上刻度加倍以补偿字符高度(ascii中的"像素"不是正方形)我已经包含了一个新的绘图函数指针参数来回调点绘图和main从命令行获取参数的例程:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void draw(int x, int y)
{
    /* move to position (2*x, y) and plot an asterisk */
    printf("\033[%d;%dH*", y, x<<1);
}

int bh(int r, int cx, int cy, void(*draw)(int, int))
{
    /* the variables mentioned in the text */
    int r2 = r*r;
    int x = 0, x2 = 0, dx2 = 1;
    int y = r, y2 = y*y, dy2 = 2*y - 1;
    int sum = r2;

    while(x <= y) {
            /* draw the eight points */
            draw(cx + x, cy + y);
            draw(cx + x, cy - y);
            draw(cx - x, cy + y);
            draw(cx - x, cy - y);
            draw(cx + y, cy + x);
            draw(cx + y, cy - x);
            draw(cx - y, cy + x);
            draw(cx - y, cy - x);

            sum -= dx2;
            x2 += dx2;
            x++;
            dx2 += 2;
            if (sum <= y2) {
                    y--; y2 -= dy2; dy2 -= 2;
            }
    } /* while */
    return x; /* number of points drawn */
}


int main(int argc, char **argv)
{
    int i;
    char *cols = getenv("COLUMNS");
    char *lines = getenv("LINES");
    int cx, cy;

    if (!cols) cols = "80";
    if (!lines) lines = "24";

    cx = atoi(cols)/4;
    cy = atoi(lines)/2;

    printf("\033[2J"); /* erase screen */

    for (i = 1; i < argc; i++) {
            bh(atoi(argv[i]), cx, cy, draw);
    }
    fflush(stdout);
    sleep(10);
    puts(""); /* force a new line */
}
Run Code Online (Sandbox Code Playgroud)

最终的结果是:

                                       *
                             * * * * *   * * * * *
                         * *                       * *
                       *                               *
                   * *                                   * * 
                 *                     *                     *
                 *             * * * *   * * * *             *
               *             *                   *             *
             *           * *                       * *           *
             *           *                           *           *
           *           *                               *           *
           *         *                                   *         *
           *         *                                   *         *
           *         *                                   *         *
           *         *                                   *         *
         *         *                                       *         *
           *         *                                   *         *
           *         *                                   *         *
           *         *                                   *         *
           *         *                                   *         *
           *           *                               *           *
             *           *                           *           *
             *           * *                       * *           *
               *             *                   *             *
                 *             * * * *   * * * *             *
                 *                     *                     *
                   * *                                   * *
                       *                               *
                         * *                       * *
                             * * * * *   * * * * *
                                       *
Run Code Online (Sandbox Code Playgroud)

最后,如果你想得到更好的结果(不是那些由精确的半径值引起的峰值,使它们只触及x或y为零时的点)你可以直接传递例程半径的平方值(允许制作小数半径的整数计算)

填一个圆圈

如果要填充圆,只需绘制一对计算点之间的所有点,如下所示:

lineFromTo(cx - x, cy - y, cx + x, cy - y);
lineFromTo(cx - y, cy + x, cx + y, cy + x);
lineFromTo(cx - y, cy - x, cx + y, cy - x);
lineFromTo(cx - x, cy + y, cx + x, cy + y);
Run Code Online (Sandbox Code Playgroud)

这些都是水平线,所以也许你可以通过以下方式获得改进:

/*               X1      X2       Y   */
HorizLineX1X2Y(cx - x, cx + x, cy - y);
HorizLineX1X2Y(cx - y, cx + y, cy + x);
HorizLineX1X2Y(cx - y, cx + y, cy - x);
HorizLineX1X2Y(cx - x, cx + x, cy + y);
Run Code Online (Sandbox Code Playgroud)

github中创建了一个新的git存储库,其中包含允许填充,绘制或跟踪算法运行的最终程序