You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

二维数组绘制椭圆时的坐标奇偶适配偏差问题

适配矩形的椭圆绘制修复(奇数尺寸补全像素)

你的问题出在整数除法导致的中心偏移和椭圆半轴的截断计算上:当矩形宽高为奇数时,width/2和height/2的整数除法会丢失0.5的偏移量,使得椭圆无法覆盖到矩形的边缘像素。

关键问题分析

  1. 宽高计算错误:width = end.X - start.X 得到的是坐标差,而非实际覆盖的像素数量,导致半轴计算偏小
  2. 整数截断:用整数存储中心和半轴,丢失了奇数尺寸下的半像素偏移,使得椭圆边缘无法触达矩形的边界

修复后的代码

private void DrawEllipse(in NexusCoord start, in NexusCoord end, in NexusChar character)
{
    ThrowIfOutOfBounds(start);
    ThrowIfOutOfBounds(end);
    GetOrThrowColorIndex(character.Foreground, character.Background, nameof(character), out var foregroundColorIndex, out var backgroundColorIndex);

    var glyph = new Glyph(character.Value, foregroundColorIndex, backgroundColorIndex);

    // 计算实际覆盖的像素宽度和高度(包含起始和结束坐标)
    var width = end.X - start.X + 1;
    var height = end.Y - start.Y + 1;

    // 用浮点类型存储中心坐标和半轴,避免整数截断
    var centerX = start.X + (width - 1) / 2.0;
    var centerY = start.Y + (height - 1) / 2.0;
    var radiusX = (width - 1) / 2.0;
    var radiusY = (height - 1) / 2.0;

    // 使用中点椭圆算法的浮点版本,避免整数精度丢失
    double x = 0;
    double y = radiusY;

    // 初始化决策参数,使用浮点计算
    double d1 = (radiusY * radiusY) - (radiusX * radiusX * radiusY) + (0.25 * radiusX * radiusX);
    double dx = 2 * radiusY * radiusY * x;
    double dy = 2 * radiusX * radiusX * y;

    while (dx < dy)
    {
        // 四舍五入到最近的整数坐标,确保覆盖边缘像素
        int drawX = (int)Math.Round(x + centerX);
        int drawY = (int)Math.Round(y + centerY);
        SetGlyph(new NexusCoord(drawX, drawY), glyph);
        SetGlyph(new NexusCoord((int)Math.Round(-x + centerX), drawY), glyph);
        SetGlyph(new NexusCoord(drawX, (int)Math.Round(-y + centerY)), glyph);
        SetGlyph(new NexusCoord((int)Math.Round(-x + centerX), (int)Math.Round(-y + centerY)), glyph);

        if (d1 < 0)
        {
            x++;
            dx += 2 * radiusY * radiusY;
            d1 += dx + radiusY * radiusY;
        }
        else
        {
            x++;
            y--;
            dx += 2 * radiusY * radiusY;
            dy -= 2 * radiusX * radiusX;
            d1 += dx - dy + radiusY * radiusY;
        }
    }

    double d2 = (radiusY * radiusY) * ((x + 0.5) * (x + 0.5)) + (radiusX * radiusX) * ((y - 1) * (y - 1)) - (radiusX * radiusX * radiusY * radiusY);

    while (y >= 0)
    {
        int drawX = (int)Math.Round(x + centerX);
        int drawY = (int)Math.Round(y + centerY);
        SetGlyph(new NexusCoord(drawX, drawY), glyph);
        SetGlyph(new NexusCoord((int)Math.Round(-x + centerX), drawY), glyph);
        SetGlyph(new NexusCoord(drawX, (int)Math.Round(-y + centerY)), glyph);
        SetGlyph(new NexusCoord((int)Math.Round(-x + centerX), (int)Math.Round(-y + centerY)), glyph);

        if (d2 > 0)
        {
            y--;
            dy -= 2 * radiusX * radiusX;
            d2 += radiusX * radiusX - dy;
        }
        else
        {
            y--;
            x++;
            dx += 2 * radiusY * radiusY;
            dy -= 2 * radiusX * radiusX;
            d2 += dx - dy + radiusX * radiusX;
        }
    }
}

核心改动说明

  • 宽高修正:width = end.X - start.X + 1 确保计算的是矩形实际覆盖的像素总数,而非坐标差
  • 浮点中心与半轴:用double存储中心和半轴,保留奇数尺寸下的半像素偏移,避免截断
  • 坐标取整:使用Math.Round()将浮点坐标转换为整数像素,确保椭圆边缘准确对齐矩形边界
  • 决策参数浮点化:所有中点算法的决策变量改用浮点计算,消除整数运算带来的精度误差

调用示例不变:

Graphic.DrawShape(NexusShape.Ellipse, new NexusCoord(10, 10), new NexusCoord(45, 35));

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 07:20:19