【问题标题】:Efficient Algorithm to obtain Points in a Circle around a Center获取围绕中心的圆圈中的点的有效算法
【发布时间】:2020-06-18 08:20:03
【问题描述】:

问题

我想获取围绕给定点的给定半径圆内的所有像素,其中点只能具有整数坐标,即画布中的像素。

所以我想获得给定(x, y)r的黄色区域内的所有点。

方法

我能想到的最有效的方法是在(x, y) 周围循环一个正方形并检查每个点的欧几里得距离

for (int px = x - r; px <= x + r; px++) {
  for (int py = y - r; py <= y + r; py++) {
    int dx = x - px, dy = y - py;

    if (dx * dx + dy * dy <= r * r) {
      // Point is part of the circle.
    }
  }
}

但是,这意味着该算法将检查不属于圆圈的(r * 2)^2 * (4 - pi) / 4 像素。 dx * dx + dy * dy &lt;= r * r,看起来相当昂贵,几乎在当时被冗余调用 1 / 4

集成 proposed here 之类的东西可能会提高性能:

for (int px = x - r; px <= x + r; px++) {
  for (int py = y - r; py <= y + r; py++) {
    int dx = abs(x - px), dy = abs(y - py);

    if (dx + dy <= r || (!(dx > r || dy > r) && (dx * dx + dy * dy <= r * r))) {
      // Point is part of the circle.
    }
  }
}

但是正如作者自己指出的那样,当大多数点都在圆内时(尤其是因为abs),这可能不会更快,在这种情况下是pi / 4


我无法找到有关此问题的任何资源。我正在专门寻找 C++ 中的解决方案,而不是 in SQL

【问题讨论】:

  • 整数乘法和加法非常快。我不确定添加一堆额外的比较是否会有所帮助,特别是因为它可能会引入所有可能的分支预测缺失。
  • 问“最多”或“最好”的问题通常无法明确回答,因为很少有单一的最佳答案。哪种解决方案最好,可能会因实际用例和将运行的架构而异。
  • 利用这样一个事实,即知道给定 y 值的 x 值范围可以让您深入了解前一个和下一个 y 值的 x 值范围。
  • 一旦你从上一个好的点移动到下一个坏的点,你至少应该能够摆脱循环。到那时你就知道你已经越过了边界,并且在那条线上没有更多的好点了。
  • 另一个选项,用于制作 Scotts 的示例,从顶部或底部中间开始,然后向左和向右循环直到到达边界。这意味着您的内部循环会在您越过边界时立即停止。

标签: c++ algorithm canvas geometry distance


【解决方案1】:

这是一个减少1/4搜索维度的优化:

for (int px = x; px <= x + r; ++px) {
  bool find = false;
  int dx = x - px, dy;
  for (int py = y; !find && py <= y + r; ++py) {
    dy = y - py;
    if (dx * dx + dy * dy <= r * r)) {
      /* (px, py), (px, y+y-py+r), (x+x-px+r, py) 
       & (x+x-px+r, y+y-py+r) are part of the circle.*/
    }else{
      find = true; //Avoid increasing on the axis y
    }
  }
}

或更好,提高性能第二圈for 的迭代避免if 条件

for (int px = x; px <= x + r; ++px) {
  int dx = x - px, py = y;
  for (; dx * dx + (py-y) * (py-y) <= r * r; ++py) {
    /* (px, py), (px, y+y-py+r), (x+x-px+r, py) 
     & (x+x-px+r, y+y-py+r) are part of the circle.*/
  }
}

我认为其他选项是对上限的二进制搜索:

int binarySearch(int R, int dx, int y){
  int l=y, r=y+R;
  while (l < r) { 
    int m = l + (r - l) / 2;  
    if(dx*dx + (y - m)*(y - m) > R*R) r = m - 1; 
    else if(dx*dx + (y - m)*(y - m) < R*R) l = m + 1; 
    else r = m;
  }
  if(dx*dx + (y - l)*(y - l) > R*R) --l;
  return l;
}

for (int px = x; px <= x + r; ++px) {
  int upperLimit = binarySearch(r, px-x, y);
  for (int py = y; py <= upperLimit; ++py) {
    /* (px, py), (px, y+y-py+r), (x+x-px+r, py) 
     & (x+x-px+r, y+y-py+r) are part of the circle.*/
  }
}

二分查找的思想是最优地找到上限,避免if条件和for循环内的计算。为此,检查哪个是使当前点与圆内半径之间的距离的最大整数。

PD:对不起我的英语。

【讨论】:

    【解决方案2】:

    您可以画一个适合圆圈内的正方形,并且很容易找到该点是否落入。

    这将在 O(1) 时间内解决大多数点 (2 * r^2),而不是搜索所有 (4 * r^2) 点。

    编辑:对于其余的点,您不需要循环所有其他像素。您需要在正方形的 4 个边(北、东、南、西)上循环 4 个尺寸为 [(2r/sqrt(2)), r-(r/sqrt(2))] 的矩形,即里面。这意味着您永远不必搜索角落上的正方形。由于它是完全对称的,我们可以取输入点的绝对值并搜索该点是否在坐标平面正侧的半正方形内。这意味着我们只循环一次而不是 4 次。

    int square_range = r/sqrt(2);
    int abs_x = abs(x);
    int abs_y = abs(y);
    
    if(abs_x < square_range && abs_y < square_range){
        //point is in
    }
    else if(abs_x < r && abs_y < r){  // if it falls in the outer square
        // this is the only loop that has to be done
        if(abs_x < abs_y){
            int temp = abs_y;
            abs_y = abs_x;
            abs_x = temp;
        }
        for(int x = r/sqrt(2) ; x < r ; x++){
            for(int y = 0 ; y < r/sqrt(2) ; y++){
                 if(x*x + y*y < r*r){
                     //point is in
                 }
             }    
        }    
    }        
    

    代码的整体复杂度为 O((r-r/sqrt(2))* (r/sqrt(2)))。仅循环在内正方形和圆的外边界之间的单个矩形(8向对称)的一半。

    【讨论】:

    • 这种方法会在圆内画一个正方形。圈子里的其他人呢?
    • 正如我提到的,广场外的其他点将被循环搜索。
    • 整体复杂度是O(n^2) 而不是O((r-r/sqrt(2))* (r/sqrt(2))) 你给出的二次方和大O 符号不应该包括系数,除了最高幂之外的所有都必须忽略。这个问题不能在O(n^2)下面简化
    • 我知道大 o 表示法,但我的意思是说,与其他 O(n^2) 相比,该算法执行的操作更少。
    【解决方案3】:

    代码

    基于@ScottHunter的想法,我想出了以下算法:

    #include <functional>
    
    // Executes point_callback for every point that is part of the circle
    // defined by the center (x, y) and radius r.
    void walk_circle(int x, int y, int r,
                     std::function<void(int x, int y)> point_callback) {
      for (int px = x - r; px < x + r; px++)
        point_callback(px, y);
      int mdx = r;
      for (int dy = 1; dy <= r; dy++)
        for (int dx = mdx; dx >= 0; dx--) {
          if (dx * dx + dy * dy > r * r)
            continue;
          for (int px = x - dx; px <= x + dx; px++) {
            point_callback(px, y + dy);
            point_callback(px, y - dy);
          }
          mdx = dx;
          break;
        }
    }
    

    算法解释

    此算法执行 分钟 次检查。具体来说,它只检查每一行,直到到达圆的第一个点。此外,它将跳过下一行中先前标识的点左侧的点。此外,通过使用对称性,只检查了一半的行(n/2 + 1/2,我们从 0 开始)。

    这是我创建的算法的可视化。红色轮廓表示之前检查过的正方形,黑色像素表示真正的圆圈(中间的红色像素是中心)。该算法检查点(标记为蓝色)并循环通过有效点(标记为绿色)。
    如您所见,最后蓝色像素的数量是分钟,即只有几个点被循环,而不是圆圈的一部分。此外,请注意每次只有第一个绿色像素需要检查,其他的只是循环通过,这就是它们立即出现的原因。

    注意事项

    显然,轴可以很容易地反转。

    这可以通过更多地利用对称性来优化,即行将与列相同(遍历所有行与遍历所有列相同,从左到右,从上到下,反之亦然,vise vera)并且从中心仅向下走四分之一行就足以准确地确定哪些点将成为圆的一部分。但是,我觉得这将带来的轻微性能提升不值得额外的代码。
    如果有人想对其进行编码,请对此答案进行修改。

    使用 cmets 编写代码

    #include <functional>
    
    // Executes point_callback for every point that is part of the circle
    // defined by the center (x, y) and radius r.
    void walk_circle(int x, int y, int r,
                     std::function<void(int x, int y)> point_callback) {
      // Walk through the whole center line as it will always be completely
      // part of the circle.
      for (int px = x - r; px < x + r; px++)
        point_callback(px, y);
      // Define a maximum delta x that shrinks whith every row as the arc
      // is closing.
      int mdx = r;
      // Start directly below the center row to make use of symmetry.
      for (int dy = 1; dy <= r; dy++)
        for (int dx = mdx; dx >= 0; dx--) {
          // Check if the point is part of the circle using Euclidean distance.
          if (dx * dx + dy * dy > r * r)
            continue;
    
          // If a point in a row left to the center is part of the circle,
          // all points to the right of it until the center are going to be
          // part of the circle as well.
          // Then, we can use horizontal symmetry to move the same distance
          // to the right from the center.
          for (int px = x - dx; px <= x + dx; px++) {
            // Use y - dy and y + dy thanks to vertical symmetry
            point_callback(px, y + dy);
            point_callback(px, y - dy);
          }
    
          // The next row will never have a point in the circle further left.
          mdx = dx;
          break;
        }
    }
    

    【讨论】:

    • JS 可能足够快,可以“蛮力”为每个圆即时计算像素坐标(可能不需要优化)。但是,您将只测试 1 个半径还是只测试几个半径?如果是,您可以将圆的内部点预先计算到一个数组中并重复使用这些计算的点——偏移到您正在测试的圆的原点。
    • 你可以看到我的代码的最后更新,我认为这更好
    【解决方案4】:
    for (line = 1; line <= r; line++) {
       dx = (int) sqrt(r * r - line * line);
       for (ix = 1; ix <= dx; ix++) {
           putpixel(x - ix, y + line)
           putpixel(x + ix, y + line)
           putpixel(x - ix, y - line)
           putpixel(x + ix, y - line)
       } 
    }
    

    为避免在轴上重复生成像素,值得从 1 开始循环并在单独的循环中绘制中心线(ix==0 或 line==0)。

    注意,也有纯整数 Bresenham 算法来生成圆周点。

    【讨论】:

    • I这里没有使用对称性的真正增益。我更改了代码以生成四分之一圆。
    【解决方案5】:

    好的,首先我们计算圆的内正方形。它的公式很简单:

    x² + y² = r²    // circle formula
    2h² = r²        // all sides of square are of equal length so x == y, lets define h := x
    h = r / sqrt(2) // half side length of the inner square
    

    现在,(-h, -h)(+h, +h) 之间的每个点都在圆圈内。这是我的意思的图像:

    剩下的蓝色部分有点棘手,但也不是太复杂。我们从蓝色圆圈(x = 0, y = -radius) 的最顶端开始。接下来,我们向右走 (x++) 直到我们离开圆圈边界(直到 x²+y² &lt; r² 不再存在)。 (0, y) 和 (x, y) 之间的所有内容都在圆圈内。由于对称性,我们可以将这 8 倍扩展

    • (-x, -y), (+x, -y)
    • (-x, +y), (+x, +y)
    • (-y, -x), (-y, +x)
    • (+y, -x), (+y, +x)

    现在我们向下移动 1 行 (y--) 并重复上述步骤(同时保持最新的 x 值)。将圆心添加到每个点,就完成了。

    这是一个可视化。由于升级,存在一些伪影。红点显示了我们在每次迭代中测试的内容:

    这是完整的代码(使用opencv绘制的东西):

    #include <opencv2/opencv.hpp>
    
    constexpr double sqrt2 = 1.41421356237309504880168;
    
    int main()
    {
        cv::Point center(200, 200);
        constexpr int radius = 180;
    
        // create test image
        cv::Mat img(400, 400, CV_8UC3);
        cv::circle(img, center, radius, {180, 0, 0}, cv::FILLED);
        cv::imshow("img", img);
        cv::waitKey();
    
        // calculate inner rectangle
        int halfSideLen = radius / sqrt2;
        cv::Rect innerRect(center.x - halfSideLen, center.y - halfSideLen, halfSideLen * 2, halfSideLen * 2);
        cv::rectangle(img, innerRect, {0, 180, 0}, cv::FILLED);
        cv::imshow("img", img);
        cv::waitKey();
    
        // probe the rest
        int x = 0;
        for (int y = radius; y >= halfSideLen; y--)
        {
            for (; x * x + y * y < radius * radius; x++)
            {
                // anything between the following points lies within the circle
                // each pair of points represents a line
                // (-x, -y), (+x, -y)
                // (-x, +y), (+x, +y)
                // (-y, -x), (-y, +x)
                // (+y, -x), (+y, +x)
    
                // center + {(-X..X) x (-Y..Y)} is inside the circle
                cv::line(img, cv::Point(center.x - x, center.y - y), cv::Point(center.x + x, center.y - y), {180, 180, 0});
                cv::line(img, cv::Point(center.x - x, center.y + y), cv::Point(center.x + x, center.y + y), {180, 180, 0});
                cv::line(img, cv::Point(center.x - y, center.y - x), cv::Point(center.x - y, center.y + x), {180, 180, 0});
                cv::line(img, cv::Point(center.x + y, center.y - x), cv::Point(center.x + y, center.y + x), {180, 180, 0});
    
                cv::imshow("img", img);
                cv::waitKey(20);
            }
        }
    
        cv::waitKey();
        return 0;
    }
    

    【讨论】:

    • @creativecreatorormaybenot 我认为除了非常小的图像之外,我的版本应该更快,因为中心正方形的计算是恒定时间(没有任何三角函数,我们只使用基本数字)。这也意味着您不必在方格内执行任何检查。对于图像的其余部分,算法基本相同。
    • 刚刚也注意到了这一点。这可能归结为汇编程序级别的微优化。也许我稍后会做一个基准测试。
    【解决方案6】:

    好的,这是我承诺的基准。

    设置

    我使用了google benchmark,任务是将圆周边内的所有点插入std::vector&lt;point&gt;。我以一组半径和一个恒定中心为基准:

    radii = {10, 20, 50, 100, 200, 500, 1000}
    center = {100, 500}
    
    • 语言:C++17
    • 编译器:msvc 19.24.28316 x64
    • 平台:windows 10
    • 优化:O2(完全优化)
    • 线程:单线程执行

    每个算法的结果都经过正确性测试(与 OPs 算法的输出相比较)。

    到目前为止,对以下算法进行了基准测试:

    1. OP的算法enclosing_square
    2. My algorithmcontaining_square.
    3. creativecreatorormaybenot's algorithmedge_walking.
    4. Mandy007's algorithmbinary_search.

    结果

    Run on (12 X 3400 MHz CPU s)
    CPU Caches:
      L1 Data 32K (x6)
      L1 Instruction 32K (x6)
      L2 Unified 262K (x6)
      L3 Unified 15728K (x1)
    -----------------------------------------------------------------------------
    Benchmark                                   Time             CPU   Iterations
    -----------------------------------------------------------------------------
    binary_search/10/manual_time              804 ns         3692 ns       888722
    binary_search/20/manual_time             2794 ns        16665 ns       229705
    binary_search/50/manual_time            16562 ns       105676 ns        42583
    binary_search/100/manual_time           66130 ns       478029 ns        10525
    binary_search/200/manual_time          389964 ns      2261971 ns         1796
    binary_search/500/manual_time         2286526 ns     15573432 ns          303
    binary_search/1000/manual_time        9141874 ns     68384740 ns           77
    edge_walking/10/manual_time               703 ns         5492 ns       998536
    edge_walking/20/manual_time              2571 ns        49807 ns       263515
    edge_walking/50/manual_time             15533 ns       408855 ns        45019
    edge_walking/100/manual_time            64500 ns      1794889 ns        10899
    edge_walking/200/manual_time           389960 ns      7970151 ns         1784
    edge_walking/500/manual_time          2286964 ns     55194805 ns          308
    edge_walking/1000/manual_time         9009054 ns    234575321 ns           78
    containing_square/10/manual_time          629 ns         4942 ns      1109820
    containing_square/20/manual_time         2485 ns        40827 ns       282058
    containing_square/50/manual_time        15089 ns       361010 ns        46311
    containing_square/100/manual_time       62825 ns      1565343 ns        10990
    containing_square/200/manual_time      381614 ns      6788676 ns         1839
    containing_square/500/manual_time     2276318 ns     45973558 ns          312
    containing_square/1000/manual_time    8886649 ns    196004747 ns           79
    enclosing_square/10/manual_time          1056 ns         4045 ns       660499
    enclosing_square/20/manual_time          3389 ns        17307 ns       206739
    enclosing_square/50/manual_time         18861 ns       106184 ns        37082
    enclosing_square/100/manual_time        76254 ns       483317 ns         9246
    enclosing_square/200/manual_time       421856 ns      2295571 ns         1654
    enclosing_square/500/manual_time      2474404 ns     15625000 ns          284
    enclosing_square/1000/manual_time     9728718 ns     68576389 ns           72
    

    代码

    完整的测试代码如下,你可以复制粘贴自己测试。 fill_circle.cpp 包含不同算法的实现。

    main.cpp

    #include <string>
    #include <unordered_map>
    #include <chrono>
    
    #include <benchmark/benchmark.h>
    
    #include "fill_circle.hpp"
    
    using namespace std::string_literals;
    
    std::unordered_map<const char*, circle_fill_func> bench_tests =
    {
        {"enclosing_square", enclosing_square},
        {"containing_square", containing_square},
        {"edge_walking", edge_walking},
        {"binary_search", binary_search},
    };
    
    std::vector<int> bench_radii = {10, 20, 50, 100, 200, 500, 1000};
    
    void postprocess(std::vector<point>& points)
    {
        std::sort(points.begin(), points.end());
        //points.erase(std::unique(points.begin(), points.end()), points.end());
    }
    
    std::vector<point> prepare(int radius)
    {
        std::vector<point> vec;
        vec.reserve(10ull * radius * radius);
        return vec;
    }
    
    void bm_run(benchmark::State& state, circle_fill_func target, int radius)
    {
        using namespace std::chrono;
        constexpr point center = {100, 500};
    
        auto expected_points = prepare(radius);
        enclosing_square(center, radius, expected_points);
        postprocess(expected_points);
    
        for (auto _ : state)
        {
            auto points = prepare(radius);
    
            auto start = high_resolution_clock::now();
            target(center, radius, points);
            auto stop = high_resolution_clock::now();
    
            postprocess(points);
            if (expected_points != points)
            {
                auto text = "Computation result incorrect. Expected size: " + std::to_string(expected_points.size()) + ". Actual size: " + std::to_string(points.size()) + ".";
                state.SkipWithError(text.c_str());
                break;
            }
    
            state.SetIterationTime(duration<double>(stop - start).count());
        }
    }
    
    int main(int argc, char** argv)
    {
        for (auto [name, target] : bench_tests)
            for (int radius : bench_radii)
                benchmark::RegisterBenchmark(name, bm_run, target, radius)->Arg(radius)->UseManualTime();
    
        benchmark::Initialize(&argc, argv);
        if (benchmark::ReportUnrecognizedArguments(argc, argv))
            return 1;
        benchmark::RunSpecifiedBenchmarks();
    }
    

    fill_circle.hpp

    #pragma once
    
    #include <vector>
    
    struct point
    {
        int x = 0;
        int y = 0;
    };
    
    constexpr bool operator<(point const& lhs, point const& rhs) noexcept
    {
        return lhs.x != rhs.x
                   ? lhs.x < rhs.x
                   : lhs.y < rhs.y;
    }
    
    constexpr bool operator==(point const& lhs, point const& rhs) noexcept
    {
        return lhs.x == rhs.x && lhs.y == rhs.y;
    }
    
    using circle_fill_func = void(*)(point const& center, int radius, std::vector<point>& points);
    
    void enclosing_square(point const& center, int radius, std::vector<point>& points);
    void containing_square(point const& center, int radius, std::vector<point>& points);
    void edge_walking(point const& center, int radius, std::vector<point>& points);
    void binary_search(point const& center, int radius, std::vector<point>& points);
    

    fill_circle.cpp

    #include "fill_circle.hpp"
    
    constexpr double sqrt2 = 1.41421356237309504880168;
    constexpr double pi = 3.141592653589793238462643;
    
    void enclosing_square(point const& center, int radius, std::vector<point>& points)
    {
        int sqr_rad = radius * radius;
    
        for (int px = center.x - radius; px <= center.x + radius; px++)
        {
            for (int py = center.y - radius; py <= center.y + radius; py++)
            {
                int dx = center.x - px, dy = center.y - py;
                if (dx * dx + dy * dy <= sqr_rad)
                    points.push_back({px, py});
            }
        }
    }
    
    void containing_square(point const& center, int radius, std::vector<point>& points)
    {
        int sqr_rad = radius * radius;
        int half_side_len = radius / sqrt2;
        int sq_x_end = center.x + half_side_len;
        int sq_y_end = center.y + half_side_len;
    
        // handle inner square
        for (int x = center.x - half_side_len; x <= sq_x_end; x++)
            for (int y = center.y - half_side_len; y <= sq_y_end; y++)
                points.push_back({x, y});
    
        // probe the rest
        int x = 0;
        for (int y = radius; y > half_side_len; y--)
        {
            int x_line1 = center.x - y;
            int x_line2 = center.x + y;
            int y_line1 = center.y - y;
            int y_line2 = center.y + y;
    
            while (x * x + y * y <= sqr_rad)
                x++;
    
            for (int i = 1 - x; i < x; i++)
            {
                points.push_back({x_line1, center.y + i});
                points.push_back({x_line2, center.y + i});
                points.push_back({center.x + i, y_line1});
                points.push_back({center.x + i, y_line2});
            }
        }
    }
    
    void edge_walking(point const& center, int radius, std::vector<point>& points)
    {
        int sqr_rad = radius * radius;
        int mdx = radius;
    
        for (int dy = 0; dy <= radius; dy++)
        {
            for (int dx = mdx; dx >= 0; dx--)
            {
                if (dx * dx + dy * dy > sqr_rad)
                    continue;
    
                for (int px = center.x - dx; px <= center.x + dx; px++)
                {
                    for (int py = center.y - dy; py <= center.y + dy; py += 2 * dy)
                    {
                        points.push_back({px, py});
                        if (dy == 0)
                            break;
                    }
                }
    
                mdx = dx;
                break;
            }
        }
    }
    
    void binary_search(point const& center, int radius, std::vector<point>& points)
    {
        constexpr auto search = []( const int &radius, const int &squad_radius, int dx, const int &y)
        {
            int l = y, r = y + radius, distance;
    
            while (l < r)
            {
                int m = l + (r - l) / 2;
                distance = dx * dx + (y - m) * (y - m);
                if (distance > squad_radius)
                    r = m - 1;
                else if (distance < squad_radius)
                    l = m + 1;
                else
                    r = m;
            }
    
            if (dx * dx + (y - l) * (y - l) > squad_radius)
                --l;
    
            return l;
        };
    
        int squad_radius = radius * radius;    
        for (int px = center.x - radius; px <= center.x + radius; ++px)
        {
            int upper_limit = search(radius, squad_radius, px - center.x, center.y);
            for (int py = 2*center.y - upper_limit; py <= upper_limit; ++py)
            {
                points.push_back({px, py});
            }
        }
    }
    

    【讨论】:

    • @creativecreatorormaybenot np.如果您调整算法,请编辑此答案,以便我们在此处更新结果。
    【解决方案7】:

    这个问题的复杂度固定为 O(n^2),其中 n 是圆的半径。与正方形或任何常规 2D 形状相同的复杂度

    不能忽略一个事实,即不能减少圆中的像素数,即使利用对称性,复杂性仍然保持不变。

    因此忽略复杂性并寻求优化。

    在您的问题中,您说abs 每像素(或第 4 像素)有点太贵了

    每行一次优于每像素一次

    您可以将其减少到每行 1 个平方根。对于一个半径为 256 的圆,即 128 个平方根

    void circle(int x, int y, int radius) {
        int y1 = y, y2 = y + 1, r = 0, rSqr = radius * radius;
        while (r < radius) {
            int x1 = x, x2 = x + 1, right = x + sqrt(rSqr - r * r) + 1.5;;
            while (x2 < right) {
                pixel(x1, y1);
                pixel(x2, y1);
                pixel(x1--, y2);
                pixel(x2++, y2);
            }
            y1--;
            y2++;
            r++;
        }
    }
    

    要从中获得更多信息,您可以为 sqrt 根计算创建一个查找表。

    所有整数

    或者,您可以使用bresenham line 的变体,将平方根替换为所有整数数学。然而,它是一团糟,除非设备没有浮点单元,否则不会有任何好处。

    void circle(int x, int y, int radius) {
        int l, yy = 0, xx = radius - 1, dx = 1, dy = 1;
        int err = dx - (radius << 1);
        int l2 = x, y0 = y, r2 = x + 1;
        int l1 = x - xx, r1 = r2 + xx;
        int y2 = y0 - xx, y1 = y0 + 1, y3 = y1 + xx;
        while (xx >= yy) {
            l = l1;
            while (l < r1) {
                pixel(l, y1);
                pixel(l++, y0);
            }
            l = l2;
            while (l < r2) {
                pixel(l, y3);
                pixel(l++, y2);
            }
            err += dy;
            dy += 2;
            y0--;
            yy++;
            y1++;
            l2--;
            r2++;
            if (err > 0) {
                dx += 2;
                err += (-radius << 1) + dx;
                xx--;
                r1--
                l1++
                y3--
                y2++
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多