C++/SDL2——渲染一个圆[关闭]

2024-01-01

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

输出图像 https://i.stack.imgur.com/xKP5j.png


首先,您的代码中有一些错误,因为sin() and cos()函数是弧度函数,而不是基于度数的函数。这将使您的圆出现在伪随机选择的点上,因为每一步都会绘制一个与前一步相距约 57 度的点。这不会产生任何影响,因为现代图形工作是缓冲的,您将看到最终结果,而不是工作原理。

话虽如此,今天没有人使用您公开的算法来画圆。看看Bresenham 中点算法 https://en.wikipedia.org/wiki/Midpoint_circle_algorithm,它基本上尝试按八分圆绘制一个圆,但速度要快几倍。

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

当我年轻时从头开始开发该算法时(以前没有见过布雷森汉姆的算法),我对解决方案的推理可能会对您有所帮助。

第一个尝试是考虑到小圆圈的分辨率(粒度与角度无关),您必须比大圆圈绘制更少的像素,并且您遵循的单度方法必须重新设计以适应更精细或粗略的分辨率。这个想法是,一个像素一个像素地进行,而不是逐度进行,直到绘制出完整的东西。我们将仅绘制第一个八分圆,并根据图形的对称性绘制其余部分。我们从(0, -R)点并将逐个像素地移动,直到我们到达(sqrt(2)*R, R - sqrt(2)*R) point.

我们要做的第一件事是尝试保存我们必须执行的所有操作。我们可以节省运算的第一个地方是计算平方......我们将使用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,只需将 2 添加到odd变量,然后添加odd到最后一个平方数,所以我们将使用两个数字:x and x2。我们将两者初始化为0,第一个增长为x += 1;,而第二个则随着关系而增长x2 += dx2; dx2 += 2;(我们初始化dx2 = 1;)这让我们允许x and x2只通过求和来增长,根本不做乘法。

如果有人认为我们需要y2 = 100 - x2然后我们被迫计算y = sqrt(y2)你是almost是的,但这里的技巧是能够管理y and y2向后序列与 x 对应序列相同。嗯,对了,y and y2可以以相同的方式进行相反的管理x and x2但这一次我们必须倒退,将奇数从(什么?)减少到1 where dy2 -= 2; y2 -= dy2;并最终达到0。为此,请检查两个连续方格之间的差值是否恰好是相加的两个数字,因此,例如,13^2 = 169 and 14^2 = 196 is 13 + 14 = 27,如果我们从R = 14 to 0 in y.

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

现在的想法是将原点设置在出发点(0, -R)然后向右,逐个像素,添加(和修改)x, x2, and sum(我们一直减去总和)直到我们到达下一个方格y,然后更新所有 y 轴值(我们确实递减 y,我们必须在那一刻向上移动一个像素)y, y2, dy2,然后绘制像素(或者之前绘制它,就像我们在例程中所做的那样),直到...什么? (嗯,重点是直到我们在 45 度点相遇,八分圆就完成了,x and y坐标相等)到此为止很重要,因为从这一点开始,一步可能使 y 坐标增加超过一个像素(导数大于一),这会使整体算法变得复杂(并且无论如何,我们正在绘制其他对称的八个点,因此我们正在绘制图形的其他部分)

因此,假设我们的半径为 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

标有星号的点是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 */
}

如果你愿意,我写了一个简单的例子,在屏幕上用 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 */
}

最终结果是:

                              *
                    * * * * *   * * * * *
                * *                       * *
              *                               *
          * *                                   * * 
        *                     *                     *
        *             * * * *   * * * *             *
      *             *                   *             *
    *           * *                       * *           *
    *           *                           *           *
  *           *                               *           *
  *         *                                   *         *
  *         *                                   *         *
  *         *                                   *         *
  *         *                                   *         *
*         *                                       *         *
  *         *                                   *         *
  *         *                                   *         *
  *         *                                   *         *
  *         *                                   *         *
  *           *                               *           *
    *           *                           *           *
    *           * *                       * *           *
      *             *                   *             *
        *             * * * *   * * * *             *
        *                     *                     *
          * *                                   * *
              *                               *
                * *                       * *
                    * * * * *   * * * * *
                              *

最后,如果您想要更好的结果(不是那些由精确的半径值产生的峰值,使它们仅在 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);

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

/*               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);

已创建一个新的 git 存储库,其中包含允许填充、绘制或跟踪算法运行的最终程序github https://github.com/mojadita/bresenham.git

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++/SDL2——渲染一个圆[关闭] 的相关文章

随机推荐

  • 如何在 python 3 中检查字符串是否为 100% ascii

    我有两个字符串 eng Clash of Clans Android Apps on Google Play rus Castle Clash Android Apps on Google Play 现在我想通过使用来检查字符串是否是英文P
  • 对象类型的 JAXB 属性抛出空指针异常?

    我正在尝试注释一个 java 类 以使用具有 value 属性的元素创建 JAXB 模式 代码如下 XmlAttribute name value public Object getSettingValue return this sett
  • qt linux "QMAKE_CXXFLAGS += -std=c++11" 相当于 Windows 吗?

    我是一个新手 所以如果这真的很愚蠢 请原谅我 最近我在ubuntu中使用qt做了一个项目 我使用 QMAKE CXXFLAGS std c 11 进行静态链接 一切都很顺利 所以我想为什么不在Windows上重新编译它 我就这么做了 这是n
  • HttpRuntime 缓存与静态字典/字段

    主要有哪些pros and cons使用 HttpRuntime Cache 反对使用简单的静态字段 我需要在整个 ASP NET 应用程序范围内存储数据 HttpRuntime Cache MyData someHashtable vs
  • 用于提高性能的代码分析:查看 mscorlib.dll 内的 CPU 周期?

    我做了一个小型测试基准来比较 NETSystem Security CryptographyAES 实现与 BouncyCastle Org 的 AES 对比 GitHub 代码链接 https github com sidshetye B
  • clojure.xml/emit 发生了什么?

    许多博客都提到了 clojure xml emit 或 clojure contrib lazy xml emit 函数 但 1 2 文档中似乎没有它 它被弃用了吗 什么取代了它 它可以用来编写 Clojure 编码的 XML 例如 tag
  • Django Forms DateInput 小部件未填充

    我有一个 django 编辑表单 其中包含模型中的许多字段 包括一些日期字段 为了获得合适的格式 我使用了 DateInput 小部件 不幸的是 当我们编辑对象时 所有其他字段都已经填充了现有数据 但日期除外 日期处于其初始状态 dd mm
  • 如何在anaconda中安装包?

    我想将音乐包添加到 anaconda 解释器中 我使用的是 ubuntu 14 04 64 位 我从anaconda云下载了music21 1 9 3 tar gz 我将其解压到 anaconda3 pkgs ext installer p
  • 将日期输入类型默认值设置为今天、明天、任意日期?

    在 HTML5 中 没有在 value 属性中指定 今天 的本机方法 这是我非常喜欢的 jQuery 代码 如何扩展此代码来设置 今天的日期到var today 明天的日期到var tomorrow 计算得出的任何日期var anydate
  • 如何在java中创建枚举对象

    我试图定义枚举类 Item 的三个不同对象 其权重在 0 到 20 之间以及每个项目的描述 这是我的代码 public enum Item Three Items with descriptions GOLD 2 gold SILVER 1
  • 如何使用Java中的文件中的特定行号读取特定行?

    在Java中 有什么方法可以从文件中读取特定行吗 例如 读取第 32 行或任何其他行号 对于小文件 String line32 Files readAllLines Paths get file txt get 32 对于大文件 try S
  • 如何在AWS EMR上重新启动yarn

    我正在使用 Hadoop 2 6 0 emr 4 2 0图像 我做了一些改变yarn site xml并希望重新启动纱线以使更改生效 有一个命令可以用来执行此操作吗 编辑 2017 年 10 月 26 日 AWS 官方已在此处发布了有关如何
  • Angular 5(Karma / Jasmine 测试)- 使用模拟后端来模拟错误消息的错误响应

    我不太明白 MockBackend 功能是如何工作的描述在这里 https angular io api http testing MockBackend 我对 Angular 完全陌生 我想编写一些测试用例 以确保当后端发生服务器错误时
  • 用于平铺渲染的 OpenGL 矩阵设置

    看完之后datenwolf 2011 年关于 OpenGL 中基于图块的渲染设置的回答 https stackoverflow com questions 6490728 capture snapshot of opengl window
  • 使用 Monkey Test 时如何防止输入设置

    我在 Android 设备 LG G4 中对我的应用程序进行了猴子测试 命令是 adb shell monkey p package name v 2000 但它可能会意外进入设置并更改我的设备的默认值 如何预防呢 Monkey支持黑名单
  • 使用 KVO 更改属性时发送通知

    我有一个名为myName在我的课堂上 比如 property nonatomic strong NSString myName 我需要在以下情况时发送通知myName属性的值发生改变 现在我正在做类似的事情 void setMyName N
  • 如何在设计时进行调试?

    我使用 UserControl 创建了一个自定义 ListView 当鼠标进入 ColumnHeader 时 它应该在设计时改变颜色 它有效 但我需要调试代码 如何在设计时调试代码 See 演练 在设计时调试自定义 Windows 窗体控件
  • 可以删除Android依赖吗?

    我有一个应用程序 到目前为止一直工作和编译得很好 但是 当我转到 导出 时 会自动添加 Android 依赖项 这会引发 转换为 Dalvik 格式失败 错误 1 我对此进行了研究 我知道这是因为 Android 依赖项添加了重复文件 我目
  • 加入具有不同排序规则问题的列

    我正在使用 SQL Server 2005 我有两个表 它们使用不同的排序规则 不允许连接具有不同排序规则的表中的列 例如不允许使用以下 SQL select table1column1 table2column2 from 我的问题是 为
  • C++/SDL2——渲染一个圆[关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 我用 SDL2 画一个实心圆 方向正确吗 我认为使用参数方程和趋于零的半径是可行的 但在处理器使用方面似乎效率很低 任何其他想法都非常感谢 提前