二维数组绘制椭圆时的坐标奇偶适配偏差问题
适配矩形的椭圆绘制修复(奇数尺寸补全像素)
你的问题出在整数除法导致的中心偏移和椭圆半轴的截断计算上:当矩形宽高为奇数时,width/2和height/2的整数除法会丢失0.5的偏移量,使得椭圆无法覆盖到矩形的边缘像素。
关键问题分析
- 宽高计算错误:
width = end.X - start.X得到的是坐标差,而非实际覆盖的像素数量,导致半轴计算偏小 - 整数截断:用整数存储中心和半轴,丢失了奇数尺寸下的半像素偏移,使得椭圆边缘无法触达矩形的边界
修复后的代码
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
相关产品推荐
相关产品推荐

