You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

关于实现八象限中点画线算法的问题(Y轴倒置场景)

解决Y轴倒置场景下的八象限中点画线算法问题

首先要给你点个赞——已经搞定了4个象限的实现,这说明你对中点画线的核心逻辑已经摸透了,剩下的问题主要是主增量方向切换Y轴倒置带来的符号调整,咱们一步步来拆解。

核心问题:剩余象限的本质是主方向从X切换到Y

你已经实现的Zone1、4、5、8都是|ΔX| ≥ |ΔY|的情况,主循环是按X递增/递减来走的;而剩下的Zone2、3、6、7都是|ΔY| > |ΔX|的情况,这时候主循环要切换为按Y递增/递减来走,决策参数和增量也要跟着调整(相当于把X和Y的角色互换)。

结合你Y轴倒置的场景(屏幕坐标系Y向下递增),我给你针对每个剩余象限的具体实现思路:

1. Zone2:X递增、Y递增、|ΔY|>|ΔX|(向右向下,Y变化更多)

void Line2D::MPOct2(const Window& window, const Point2D& start, const Point2D& end, const float& delta_x, const float& delta_y) const {
    Color color = mStartVtx.GetColor();
    int int_delta_x = static_cast<int>(round(delta_x)); // 正
    int int_delta_y = static_cast<int>(round(delta_y)); // 正
    int start_x = static_cast<int>(round(start.GetX()));
    int start_y = static_cast<int>(round(start.GetY()));
    int const end_y = static_cast<int>(round(end.GetY()));

    int f = 2 * int_delta_x - int_delta_y;
    int f_se = 2 * (int_delta_x - int_delta_y); // 选SE(X+1,Y+1)的增量
    int f_s = 2 * int_delta_x; // 选S(Y+1)的增量

    while (start_y <= end_y) {
        window.SetPixel(start_x, start_y, color);
        if (f >= 0) {
            start_x++;
            start_y++;
            f += f_se;
        } else {
            start_y++;
            f += f_s;
        }
    }
}

2. Zone3:X递减、Y递增、|ΔY|>|ΔX|(向左向下,Y变化更多)

void Line2D::MPOct3(const Window& window, const Point2D& start, const Point2D& end, const float& delta_x, const float& delta_y) const {
    Color color = mStartVtx.GetColor();
    int int_delta_x = static_cast<int>(round(delta_x)); // 负
    int int_delta_y = static_cast<int>(round(delta_y)); // 正
    int start_x = static_cast<int>(round(start.GetX()));
    int start_y = static_cast<int>(round(start.GetY()));
    int const end_y = static_cast<int>(round(end.GetY()));

    int f = -2 * int_delta_x - int_delta_y; // -2*int_delta_x等价于2*|ΔX|
    int f_sw = 2 * (-int_delta_x - int_delta_y); // 选SW(X-1,Y+1)的增量
    int f_s = -2 * int_delta_x; // 选S(Y+1)的增量

    while (start_y <= end_y) {
        window.SetPixel(start_x, start_y, color);
        if (f >= 0) {
            start_x--;
            start_y++;
            f += f_sw;
        } else {
            start_y++;
            f += f_s;
        }
    }
}

3. Zone6:X递减、Y递减、|ΔY|>|ΔX|(向左向上,Y变化更多)

void Line2D::MPOct6(const Window& window, const Point2D& start, const Point2D& end, const float& delta_x, const float& delta_y) const {
    Color color = mStartVtx.GetColor();
    int int_delta_x = static_cast<int>(round(delta_x)); // 负
    int int_delta_y = static_cast<int>(round(delta_y)); // 负
    int start_x = static_cast<int>(round(start.GetX()));
    int start_y = static_cast<int>(round(start.GetY()));
    int const end_y = static_cast<int>(round(end.GetY()));

    int f = -2 * int_delta_x + int_delta_y; // +int_delta_y等价于-2*|ΔY|
    int f_nw = 2 * (-int_delta_x + int_delta_y); // 选NW(X-1,Y-1)的增量
    int f_n = -2 * int_delta_x; // 选N(Y-1)的增量

    while (start_y >= end_y) {
        window.SetPixel(start_x, start_y, color);
        if (f >= 0) {
            start_x--;
            start_y--;
            f += f_nw;
        } else {
            start_y--;
            f += f_n;
        }
    }
}

4. Zone7:X递增、Y递减、|ΔY|>|ΔX|(向右向上,Y变化更多)

void Line2D::MPOct7(const Window& window, const Point2D& start, const Point2D& end, const float& delta_x, const float& delta_y) const {
    Color color = mStartVtx.GetColor();
    int int_delta_x = static_cast<int>(round(delta_x)); // 正
    int int_delta_y = static_cast<int>(round(delta_y)); // 负
    int start_x = static_cast<int>(round(start.GetX()));
    int start_y = static_cast<int>(round(start.GetY()));
    int const end_y = static_cast<int>(round(end.GetY()));

    int f = 2 * int_delta_x + int_delta_y; // +int_delta_y等价于-2*|ΔY|
    int f_ne = 2 * (int_delta_x + int_delta_y); // 选NE(X+1,Y-1)的增量
    int f_n = 2 * int_delta_x; // 选N(Y-1)的增量

    while (start_y >= end_y) {
        window.SetPixel(start_x, start_y, color);
        if (f >= 0) {
            start_x++;
            start_y--;
            f += f_ne;
        } else {
            start_y--;
            f += f_n;
        }
    }
}

更优方案:用坐标变换统一处理所有象限

写8份代码确实容易出错,更高效的方式是把任意象限的线段转换为Zone1的情况,计算后再映射回原坐标,这样只需要维护一份Zone1的代码:

void Line2D::MPLine(const Window& window, const Point2D& start, const Point2D& end) const {
    Color color = mStartVtx.GetColor();
    float dx_float = end.GetX() - start.GetX();
    float dy_float = end.GetY() - start.GetY();
    int dx = static_cast<int>(round(dx_float));
    int dy = static_cast<int>(round(dy_float));

    int x0 = static_cast<int>(round(start.GetX()));
    int y0 = static_cast<int>(round(start.GetY()));

    // 确定变换参数
    int sx = dx >= 0 ? 1 : -1;
    int sy = dy >= 0 ? 1 : -1;
    int abs_dx = abs(dx);
    int abs_dy = abs(dy);
    bool swap = false;

    // 如果Y变化更大,交换X和Y
    if (abs_dy > abs_dx) {
        swap = true;
        std::swap(abs_dx, abs_dy);
    }

    // 用Zone1的逻辑计算变换后的点
    int f = 2 * abs_dy - abs_dx;
    int f_e = 2 * abs_dy;
    int f_ne = 2 * (abs_dy - abs_dx);
    int x = 0, y = 0;

    while (x <= abs_dx) {
        // 映射回原始坐标
        int draw_x = x0 + (swap ? sx * y : sx * x);
        int draw_y = y0 + (swap ? sy * x : sy * y);
        window.SetPixel(draw_x, draw_y, color);

        if (f >= 0) {
            y++;
            f += f_ne;
        } else {
            f += f_e;
        }
        x++;
    }
}

这个方案的核心是通过符号翻转坐标交换,把所有象限的线段都转换成Zone1(X递增、Y递增、|X|≥|Y|)的标准情况,用你已经验证过的Zone1逻辑计算点,最后再映射回原始屏幕坐标,完美解决Y轴倒置的问题,还能大幅减少代码量。

内容的提问来源于stack exchange,提问作者user3770234

火山引擎 最新活动