c++ Bresenham直线算法绘制圆弧并旋转

发布于 2024-12-20 07:54:53 字数 1065 浏览 2 评论 0原文

我正在寻找用 Bresenham 的直线算法制作圆弧的方法。这个算法绘制完美的圆,但是如果我需要绘制圆弧(从 0 到 Pi)并将其旋转 30 度(例如)怎么办?

void DrawCircle(HDC hdc,int x0, int y0, int radius) 
{
        int x = 0;
        int y = radius;
        int delta = 2 - 2 * radius;
        int error = 0;

        while(y >= 0) {
                //SetPixel(hdc,x0 + x, y0 + y,pencol);
                SetPixel(hdc,x0 + x, y0 - y,pencol);
                //SetPixel(hdc,x0 - x, y0 + y,pencol);
                SetPixel(hdc,x0 - x, y0 - y,pencol);
                error = 2 * (delta + y) - 1;
                if(delta < 0 && error <= 0) {
                        ++x;
                        delta += 2 * x + 1;
                        continue;
                }
                error = 2 * (delta - x) - 1;
                if(delta > 0 && error > 0) {
                        --y;
                        delta += 1 - 2 * y;
                        continue;
                }
                ++x;
                delta += 2 * (x - y);
                --y;
        }
}

I'm searching way to make arc with Bresenham's line algorithm. This algoritm draw perfect circle, but what if i need draw arc (from 0 to Pi) and rotate it for 30 degrees (for example)?

void DrawCircle(HDC hdc,int x0, int y0, int radius) 
{
        int x = 0;
        int y = radius;
        int delta = 2 - 2 * radius;
        int error = 0;

        while(y >= 0) {
                //SetPixel(hdc,x0 + x, y0 + y,pencol);
                SetPixel(hdc,x0 + x, y0 - y,pencol);
                //SetPixel(hdc,x0 - x, y0 + y,pencol);
                SetPixel(hdc,x0 - x, y0 - y,pencol);
                error = 2 * (delta + y) - 1;
                if(delta < 0 && error <= 0) {
                        ++x;
                        delta += 2 * x + 1;
                        continue;
                }
                error = 2 * (delta - x) - 1;
                if(delta > 0 && error > 0) {
                        --y;
                        delta += 1 - 2 * y;
                        continue;
                }
                ++x;
                delta += 2 * (x - y);
                --y;
        }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

じ违心 2024-12-27 07:54:53

如果您确实不需要 Bresenham,则在此 SO 中引入了一种快速步骤方法 post,可以设置中心点、起点和圆弧角度。它不需要停止标准,因为它已经包含在算法中(通过弧角)。其速度快的原因是切向和径向运动因子的预先计算,并且实际循环没有三角函数调用,只有乘法、加法和减法。

AFAIK 有三种类型的方法:
A) 增量式,如 Bresenham
B) 细分方法,如 this
C)步进(或分段)方法

我将举一个缓慢的步进方法示例(如果速度很重要,请不要使用此方法):

// I know the question is tagged c++, but the idea comes clear in javascript
var start_angle = 0.5, end_angle = 1.1, r = 30;
for(var i = start_angle; i < end_angle; i = i + 0.05)
{
  drawpixel(x: 50 + Math.cos(i) * r, y: 100 + Math.sin(i) * r); // center point is (x = 50, y = 100)
}

缓慢来自于在循环中重复(不必要)的 cos 和 sin 。这可以通过预先计算 cos 和 sin 来解决,如上述 SO 帖子中所述。这意味着巨大的加速(前 5 名 JavaScript 引擎平均加速 12 倍)。

我对各种圆形和圆弧绘制算法进行了非完全可比的速度测试。 Bresenham速度很快,但需要添加启动和停止标准逻辑,这会稍微减慢算法速度。如果你真的需要 Bresenham 和 arc,我没有现成的解决方案,而且还没有找到。这当然是可能的。顺便说一句,与 Bresenham 相比,使用预先计算的三角函数的步骤方法的性能并不算差(至少在 javascript 中)。请用 c++ 进行测试并报告。

If you don't need for sure Bresenham, there is a fast step method introduced in this SO post, where you can set center point, starting point and arc angle. It doesn't need stopping criterion, because it is already included in algorithm (by arc angle). What makes it fast is the precalculation of tangential and radial movement factors and the actual loop has no trig function calls, only multiply, add and subtract.

AFAIK there is three types of methods:
A) Incremental like Bresenham
B) Subdivide method like this
C) Step (or segment) method

I'll take an slow example of step method (don't use this if speed is important):

// I know the question is tagged c++, but the idea comes clear in javascript
var start_angle = 0.5, end_angle = 1.1, r = 30;
for(var i = start_angle; i < end_angle; i = i + 0.05)
{
  drawpixel(x: 50 + Math.cos(i) * r, y: 100 + Math.sin(i) * r); // center point is (x = 50, y = 100)
}

The slowness comes from cos and sin which are repeated (unnecessarily) in loop. This can be solved by precalculating cos and sin as described in the above mentioned SO post. This means huge speedup (average 12x in top5 javascript engines).

I made a non-full-comparable speedtest of various circle and arc drawing algorithms. The Bresenham is fast, but the starting and stopping criterion logic need to be added, which slows down the algo a little. If you really need Bresenham and arc, I have no ready solution for this and not found such yet. It surely is possible. By the way, the step method using precalculated trigs is not so bad in performance compared to Bresenham (in javascript at least). Please test in c++ and report.

东风软 2024-12-27 07:54:53

要获得 1/2 圆(到 pi),只需调用 SetPixel 例程之一。要使弧线旋转 30 度,需要一些三角技术。您可以让上面的循环运行,直到您的 x/y 比率等于 tan(30 度),然后开始实际绘制,直到您的比率达到您想要停止的值。这不是最有效的方法,但它会起作用。为了获得更好的效果,您需要预先计算起始的 4 个 var 值。您可以从上面的运行中获取值并将它们作为起始值插入,这将非常有效。

您是否从 Michael Abrash 的黑皮书 内容?如果没有,我会在谷歌上搜索它作为快速圆/弧绘图的第二个参考点。

好吧,唉,撕开章节的省略号没有包含在其中。以下是我在网上找到的声称来自 Abrash 的内容:


/* One of Abrash's ellipse algorithms  */

void draw_ellipse(int x, int y, int a, int b, int color)
{
    int wx, wy;
    int thresh;
    int asq = a * a;
    int bsq = b * b;
    int xa, ya;

    draw_pixel(x, y+b, color);
    draw_pixel(x, y-b, color);

    wx = 0;
    wy = b;
    xa = 0;
    ya = asq * 2 * b;
    thresh = asq / 4 - asq * b;

    for (;;) {
        thresh += xa + bsq;

        if (thresh >= 0) {
            ya -= asq * 2;
            thresh -= ya;
            wy--;
        }

        xa += bsq * 2;
        wx++;

        if (xa >= ya)
          break;


        draw_pixel(x+wx, y-wy, color);
        draw_pixel(x-wx, y-wy, color);
        draw_pixel(x+wx, y+wy, color);
        draw_pixel(x-wx, y+wy, color);
    }

    draw_pixel(x+a, y, color);
    draw_pixel(x-a, y, color);

    wx = a;
    wy = 0;
    xa = bsq * 2 * a;

    ya = 0;
    thresh = bsq / 4 - bsq * a;

    for (;;) {
        thresh += ya + asq;

        if (thresh >= 0) {
            xa -= bsq * 2;
            thresh = thresh - xa;
            wx--;
        }

        ya += asq * 2;
        wy++;

        if (ya > xa)
          break;

        draw_pixel(x+wx, y-wy, color);
        draw_pixel(x-wx, y-wy, color);
        draw_pixel(x+wx, y+wy, color);
        draw_pixel(x-wx, y+wy, color);
    }
}

其想法是,一次绘制 8 个圆的 x4,然后翻转以绘制其他 8 个圆。但仍然没有直接回答你的问题。正在努力...

同样,上面的代码应该可以工作,您只需要仔细控制开始和结束条件即可。 y >= 0 需要成为完成“弧”长度后 y 的值,并且需要计算起始值作为弧的起点。

就目前的情况而言,这并不是一项直接的任务。使用浮点例程可能会更容易。与设计这些整数例程时相比,数学更加直接,处理器现在往往可以更好地处理它们。

To get 1/2 a circle (to pi), only call one of your SetPixel routines. To have your arc rotated 30 degrees requires some trig. You could let the above loop run until your x/y ratio is equal to tan(30 degrees), then start actually drawing until your ratio hits the value at which you want to stop. Not the most efficient way, but it will work. To get it better, you'd need to pre-calculate your starting 4 var values. You could take the values from the above run and plug them in as starting values and that would be very efficient.

Did you get the above algorithm from Michael Abrash's Black Book stuff? If not, I'd google for that as a second point of reference on fast circle/arc drawing.

Well, alas, the ellipses that rip chapter wasn't included in there. Here's something I found on the web that claims to be from Abrash:


/* One of Abrash's ellipse algorithms  */

void draw_ellipse(int x, int y, int a, int b, int color)
{
    int wx, wy;
    int thresh;
    int asq = a * a;
    int bsq = b * b;
    int xa, ya;

    draw_pixel(x, y+b, color);
    draw_pixel(x, y-b, color);

    wx = 0;
    wy = b;
    xa = 0;
    ya = asq * 2 * b;
    thresh = asq / 4 - asq * b;

    for (;;) {
        thresh += xa + bsq;

        if (thresh >= 0) {
            ya -= asq * 2;
            thresh -= ya;
            wy--;
        }

        xa += bsq * 2;
        wx++;

        if (xa >= ya)
          break;


        draw_pixel(x+wx, y-wy, color);
        draw_pixel(x-wx, y-wy, color);
        draw_pixel(x+wx, y+wy, color);
        draw_pixel(x-wx, y+wy, color);
    }

    draw_pixel(x+a, y, color);
    draw_pixel(x-a, y, color);

    wx = a;
    wy = 0;
    xa = bsq * 2 * a;

    ya = 0;
    thresh = bsq / 4 - bsq * a;

    for (;;) {
        thresh += ya + asq;

        if (thresh >= 0) {
            xa -= bsq * 2;
            thresh = thresh - xa;
            wx--;
        }

        ya += asq * 2;
        wy++;

        if (ya > xa)
          break;

        draw_pixel(x+wx, y-wy, color);
        draw_pixel(x-wx, y-wy, color);
        draw_pixel(x+wx, y+wy, color);
        draw_pixel(x-wx, y+wy, color);
    }
}

The idea being you draw an 8th of the circle at a time x4 and then flip to get the other 8ths drawn. Still doesn't directly answer your question though. Working on that...

Again, your code above should work, you just need to control the starting and ending conditions carefully. The y >= 0 needs to become whatever the y would be upon finishing your 'arc' length and the starting values need to be calculated to be the start of your arc.

This will not be a straight forward task with things as they are. Might just be easier to use a floating point routine instead. The math is much more straight forward and processors tend to handle them better now than when these integer routines were crafted.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文