C# 填充圆的中点圆算法
可以使用栅格化圆的边界。但是,我希望圆形被填充,而不需要多次绘制像素(这非常重要) 此答案对算法进行了修改,生成了一个填充圆,但某些像素会被访问几次: Q:如何在不多次绘制像素的情况下对圆进行光栅化?请注意,RAM非常有限 更新:C# 填充圆的中点圆算法,c#,algorithm,graphics,geometry,C#,Algorithm,Graphics,Geometry,可以使用栅格化圆的边界。但是,我希望圆形被填充,而不需要多次绘制像素(这非常重要) 此答案对算法进行了修改,生成了一个填充圆,但某些像素会被访问几次: Q:如何在不多次绘制像素的情况下对圆进行光栅化?请注意,RAM非常有限 更新: using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace CircleTest { class Program {
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace CircleTest
{
class Program
{
static void Main(string[] args)
{
byte[,] buffer = new byte[50, 50];
circle(buffer, 25, 25, 20);
for (int y = 0; y < 50; ++y)
{
for (int x = 0; x < 50; ++x)
Console.Write(buffer[y, x].ToString());
Console.WriteLine();
}
}
// 'cx' and 'cy' denote the offset of the circle center from the origin.
static void circle(byte[,] buffer, int cx, int cy, int radius)
{
int error = -radius;
int x = radius;
int y = 0;
// The following while loop may altered to 'while (x > y)' for a
// performance benefit, as long as a call to 'plot4points' follows
// the body of the loop. This allows for the elimination of the
// '(x != y)' test in 'plot8points', providing a further benefit.
//
// For the sake of clarity, this is not shown here.
while (x >= y)
{
plot8points(buffer, cx, cy, x, y);
error += y;
++y;
error += y;
// The following test may be implemented in assembly language in
// most machines by testing the carry flag after adding 'y' to
// the value of 'error' in the previous step, since 'error'
// nominally has a negative value.
if (error >= 0)
{
error -= x;
--x;
error -= x;
}
}
}
static void plot8points(byte[,] buffer, int cx, int cy, int x, int y)
{
plot4points(buffer, cx, cy, x, y);
if (x != y) plot4points(buffer, cx, cy, y, x);
}
// The '(x != 0 && y != 0)' test in the last line of this function
// may be omitted for a performance benefit if the radius of the
// circle is known to be non-zero.
static void plot4points(byte[,] buffer, int cx, int cy, int x, int y)
{
#if false // Outlined circle are indeed plotted correctly!
setPixel(buffer, cx + x, cy + y);
if (x != 0) setPixel(buffer, cx - x, cy + y);
if (y != 0) setPixel(buffer, cx + x, cy - y);
if (x != 0 && y != 0) setPixel(buffer, cx - x, cy - y);
#else // But the filled version plots some pixels multiple times...
horizontalLine(buffer, cx - x, cy + y, cx + x);
//if (x != 0) setPixel(buffer, cx - x, cy + y);
//if (y != 0) setPixel(buffer, cx + x, cy - y);
//if (x != 0 && y != 0) setPixel(buffer, cx - x, cy - y);
#endif
}
static void setPixel(byte[,] buffer, int x, int y)
{
buffer[y, x]++;
}
static void horizontalLine(byte[,] buffer, int x0, int y0, int x1)
{
for (int x = x0; x <= x1; ++x)
setPixel(buffer, x, y0);
}
}
}
static void circle(byte[,] buffer, int cx, int cy, int radius)
{
int error = -radius;
int x = radius;
int y = 0;
while (x >= y)
{
int lastY = y;
error += y;
++y;
error += y;
plot4points(buffer, cx, cy, x, lastY);
if (error >= 0)
{
if (x != lastY)
plot4points(buffer, cx, cy, lastY, x);
error -= x;
--x;
error -= x;
}
}
}
static void plot4points(byte[,] buffer, int cx, int cy, int x, int y)
{
horizontalLine(buffer, cx - x, cy + y, cx + x);
if (y != 0)
horizontalLine(buffer, cx - x, cy - y, cx + x);
}
底部像素的打印次数过多。我错过了什么
更新#2:此解决方案有效:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace CircleTest
{
class Program
{
static void Main(string[] args)
{
byte[,] buffer = new byte[50, 50];
circle(buffer, 25, 25, 20);
for (int y = 0; y < 50; ++y)
{
for (int x = 0; x < 50; ++x)
Console.Write(buffer[y, x].ToString());
Console.WriteLine();
}
}
// 'cx' and 'cy' denote the offset of the circle center from the origin.
static void circle(byte[,] buffer, int cx, int cy, int radius)
{
int error = -radius;
int x = radius;
int y = 0;
// The following while loop may altered to 'while (x > y)' for a
// performance benefit, as long as a call to 'plot4points' follows
// the body of the loop. This allows for the elimination of the
// '(x != y)' test in 'plot8points', providing a further benefit.
//
// For the sake of clarity, this is not shown here.
while (x >= y)
{
plot8points(buffer, cx, cy, x, y);
error += y;
++y;
error += y;
// The following test may be implemented in assembly language in
// most machines by testing the carry flag after adding 'y' to
// the value of 'error' in the previous step, since 'error'
// nominally has a negative value.
if (error >= 0)
{
error -= x;
--x;
error -= x;
}
}
}
static void plot8points(byte[,] buffer, int cx, int cy, int x, int y)
{
plot4points(buffer, cx, cy, x, y);
if (x != y) plot4points(buffer, cx, cy, y, x);
}
// The '(x != 0 && y != 0)' test in the last line of this function
// may be omitted for a performance benefit if the radius of the
// circle is known to be non-zero.
static void plot4points(byte[,] buffer, int cx, int cy, int x, int y)
{
#if false // Outlined circle are indeed plotted correctly!
setPixel(buffer, cx + x, cy + y);
if (x != 0) setPixel(buffer, cx - x, cy + y);
if (y != 0) setPixel(buffer, cx + x, cy - y);
if (x != 0 && y != 0) setPixel(buffer, cx - x, cy - y);
#else // But the filled version plots some pixels multiple times...
horizontalLine(buffer, cx - x, cy + y, cx + x);
//if (x != 0) setPixel(buffer, cx - x, cy + y);
//if (y != 0) setPixel(buffer, cx + x, cy - y);
//if (x != 0 && y != 0) setPixel(buffer, cx - x, cy - y);
#endif
}
static void setPixel(byte[,] buffer, int x, int y)
{
buffer[y, x]++;
}
static void horizontalLine(byte[,] buffer, int x0, int y0, int x1)
{
for (int x = x0; x <= x1; ++x)
setPixel(buffer, x, y0);
}
}
}
static void circle(byte[,] buffer, int cx, int cy, int radius)
{
int error = -radius;
int x = radius;
int y = 0;
while (x >= y)
{
int lastY = y;
error += y;
++y;
error += y;
plot4points(buffer, cx, cy, x, lastY);
if (error >= 0)
{
if (x != lastY)
plot4points(buffer, cx, cy, lastY, x);
error -= x;
--x;
error -= x;
}
}
}
static void plot4points(byte[,] buffer, int cx, int cy, int x, int y)
{
horizontalLine(buffer, cx - x, cy + y, cx + x);
if (y != 0)
horizontalLine(buffer, cx - x, cy - y, cx + x);
}
另一个问题的答案很好。然而,由于这会造成混乱,我将对此进行一点解释 你在维基百科中看到的算法基本上是找到一个圆的1/8(角度0到pi/4),然后画出8个点作为它的镜像。例如:
(o-y,o+x) x x (o+y,o+x)
(o-x,o+y) x x (o+x,o+y) <-- compute x,y
o
(o-x,o-y) x x (o+x,o-y)
(o-y,o-x) x x (o+y,o-x)
(o-y,o+x)x(o+y,o+x)
(o-x,o+y)x(o+x,o+y)我需要这样做,下面是我为它准备的代码。这里的可视图像显示绘制的像素,其中数字是像素的遍历顺序,绿色数字表示使用对称性的列的完成反射绘制的像素,如代码所示
void drawFilledMidpointCircleSinglePixelVisit( int centerX, int centerY, int radius )
{
int x = radius;
int y = 0;
int radiusError = 1 - x;
while (x >= y) // iterate to the circle diagonal
{
// use symmetry to draw the two horizontal lines at this Y with a special case to draw
// only one line at the centerY where y == 0
int startX = -x + centerX;
int endX = x + centerX;
drawHorizontalLine( startX, endX, y + centerY );
if (y != 0)
{
drawHorizontalLine( startX, endX, -y + centerY );
}
// move Y one line
y++;
// calculate or maintain new x
if (radiusError<0)
{
radiusError += 2 * y + 1;
}
else
{
// we're about to move x over one, this means we completed a column of X values, use
// symmetry to draw those complete columns as horizontal lines at the top and bottom of the circle
// beyond the diagonal of the main loop
if (x >= y)
{
startX = -y + 1 + centerX;
endX = y - 1 + centerX;
drawHorizontalLine( startX, endX, x + centerY );
drawHorizontalLine( startX, endX, -x + centerY );
}
x--;
radiusError += 2 * (y - x + 1);
}
}
}
void drawfilledMiddpointCircleSinglePixelvisit(int centerX、int centerY、int radius)
{
int x=半径;
int y=0;
int radiusError=1-x;
while(x>=y)//迭代到对角圆
{
//使用“对称”在Y轴上绘制两条水平线,并使用特殊情况进行绘制
//在中心处只有一行,其中y==0
int startX=-x+centerX;
int endX=x+centerX;
绘制水平线(起点、终点、y+中心线);
如果(y!=0)
{
绘制水平线(起点,终点,-y+中心线);
}
//移动Y一行
y++;
//计算或维护新的x
如果(radiusError我想出了一个算法,可以画出已经填充的圆。
它在将绘制圆的像素上迭代,而不在其他像素上迭代。
从这里开始,所有关于绘制像素函数的速度
至于算法,代码如下:
//The center of the circle and its radius.
int x = 100;
int y = 100;
int r = 50;
//This here is sin(45) but i just hard-coded it.
float sinus = 0.70710678118;
//This is the distance on the axis from sin(90) to sin(45).
int range = r/(2*sinus);
for(int i = r ; i >= range ; --i)
{
int j = sqrt(r*r - i*i);
for(int k = -j ; k <= j ; k++)
{
//We draw all the 4 sides at the same time.
PutPixel(x-k,y+i);
PutPixel(x-k,y-i);
PutPixel(x+i,y+k);
PutPixel(x-i,y-k);
}
}
//To fill the circle we draw the circumscribed square.
range = r*sinus;
for(int i = x - range + 1 ; i < x + range ; i++)
{
for(int j = y - range + 1 ; j < y + range ; j++)
{
PutPixel(i,j);
}
}
//圆心及其半径。
int x=100;
int y=100;
int r=50;
//这是罪(45),但我只是硬编码。
浮窦=0.70710678118;
//这是轴上从sin(90)到sin(45)的距离。
int范围=r/(2*窦);
对于(int i=r;i>=range;--i)
{
int j=sqrt(r*r-i*i);
对于(int k=-j;k我想对您的更新发表评论#2:此解决方案有效:(但我想我首先需要更多的声誉…)解决方案中存在一个小错误,在绘制小圆圈时正好如此。如果您将半径设置为1,则得到
00000
00000
01110
00100
00000
要解决此问题,只需将Plot4 Points中的条件签入更改为
if (x != 0 && y != 0)
到
我已经在大小圆圈上测试过,以确保每个像素只分配一次。似乎效果很好。我认为不需要x!=0。还可以节省一点性能。更新#2
到
圆形和圆角圆形版本:
Const
Vypln13:Boolean=False; // Fill Object
//Draw a circle at (cx,cy)
Procedure Circle(cx: integer; cy: integer; radius: integer );
Var
error,x,y: integer;
Begin
error := -radius;
x := radius;
y := 0;
while (x >= y) do
Begin
Draw4Pixel(cx,cy, x, y);
if ( Not Vypln13 And ( x <> y) ) Then Draw4Pixel(cx,cy, y, x);
error := error + y;
y := y + 1;
error := error + y;
if (error >= 0) Then
Begin
if ( Vypln13) then Draw4Pixel(cx, cy, y - 1, x);
error := error - x;
x := x - 1;
error := error - x;
End;
End;
End;
Procedure Draw4Pixel(cx,cy,dx,dy: integer);
Begin
if ( (dx = 0) And (dy = 0) ) then
begin
PutPixel (cx , cy , Color13);
exit;
End;
IF Vypln13 Then
Begin
HorizontLine (cx - dx, cx + dx, cy + dy, Color13);
if ( dy = 0 ) then exit;
HorizontLine (cx - dx, cx + dx, cy - dy, Color13);
exit;
end;
PutPixel (cx + dx, cy + dy, Color13);
if ( dx <> 0 ) then
begin
PutPixel (cx - dx, cy + dy, Color13);
if ( dy = 0 ) then exit;
PutPixel (cx + dx, cy - dy, Color13);
End;
PutPixel (cx - dx, cy - dy, Color13);
End;
Const
Vypln13:Boolean=False;//填充对象
//在(cx,cy)处画一个圆
程序圆(cx:整数;cy:整数;半径:整数);
变量
误差x,y:整数;
开始
误差:=-半径;
x:=半径;
y:=0;
而(x>=y)做
开始
Draw4Pixel(cx,cy,x,y);
如果(不是Vypln13和(x y)),则绘制4像素(cx,cy,y,x);
误差:=误差+y;
y:=y+1;
误差:=误差+y;
如果(错误>=0),则
开始
如果是(Vypln13),则绘制4像素(cx,cy,y-1,x);
错误:=错误-x;
x:=x-1;
错误:=错误-x;
结束;
结束;
结束;
程序Draw4Pixel(cx,cy,dx,dy:整数);
开始
如果((dx=0)和(dy=0)),则
开始
像素(cx,cy,Color13);
出口
结束;
如果Vypln13那么
开始
水平线(cx-dx、cx+dx、cy+dy、Color13);
如果(dy=0),则退出;
水平线(cx-dx、cx+dx、cy-dy、Color13);
出口
结束;
像素(cx+dx、cy+dy、Color13);
如果(dx 0)那么
开始
像素(cx-dx,cy+dy,Color13);
如果(dy=0),则退出;
像素(cx+dx,cy-dy,彩色13);
结束;
像素(cx-dx,cy-dy,Color13);
结束;
答案几次都没有访问任何像素。你为什么这么说?我的实现一直在圆圈的顶部/底部进行多重打印。也许我只是不理解答案?谢谢。我将用示例代码更新我的问题,以演示该问题。我一定缺少某个条件。嗯。所以我需要两个“前一行”变量?一个用于“y”,一个用于“x”(镜像)好吧,这不是那么简单。画一条线你需要外部像素。而且算法不能保证最外层像素被首先访问。所以如果我跟踪访问的行,一些像素将被丢失。@ NopSLoad,你只需要1个,这是针对<代码> x>代码>。让我们考虑一下来自维基百科的同一个图像。你从角度0开始,所以你有“COD”。e> (x,0)
然后开始向上移动。向上移动时,对于一些像素,x
保持不变,y
发生变化。仍将绘制2条水平线,其余两条(在y=x线上镜像)必须检查它们是否引入了水平线。如果引入了水平线,则绘制它们。此外,您是对的,您从内到外访问节点。我将更新回答谢谢,但请注意sqrt()调用是非常昂贵的。乘法和除法同样如此。在x86中,乘法、除法和SqRT都是单指令。单个指令并不一定意味着高性能。要考虑时钟周期、精度问题、平台差异……
if (error >= 0)
{
if (x != lastY)
plot4points(buffer, cx, cy, lastY, x);
if (error >= 0)
{
plot4points(buffer, cx, cy, lastY, x);
Const
Vypln13:Boolean=False; // Fill Object
//Draw a circle at (cx,cy)
Procedure Circle(cx: integer; cy: integer; radius: integer );
Var
error,x,y: integer;
Begin
error := -radius;
x := radius;
y := 0;
while (x >= y) do
Begin
Draw4Pixel(cx,cy, x, y);
if ( Not Vypln13 And ( x <> y) ) Then Draw4Pixel(cx,cy, y, x);
error := error + y;
y := y + 1;
error := error + y;
if (error >= 0) Then
Begin
if ( Vypln13) then Draw4Pixel(cx, cy, y - 1, x);
error := error - x;
x := x - 1;
error := error - x;
End;
End;
End;
Procedure Draw4Pixel(cx,cy,dx,dy: integer);
Begin
if ( (dx = 0) And (dy = 0) ) then
begin
PutPixel (cx , cy , Color13);
exit;
End;
IF Vypln13 Then
Begin
HorizontLine (cx - dx, cx + dx, cy + dy, Color13);
if ( dy = 0 ) then exit;
HorizontLine (cx - dx, cx + dx, cy - dy, Color13);
exit;
end;
PutPixel (cx + dx, cy + dy, Color13);
if ( dx <> 0 ) then
begin
PutPixel (cx - dx, cy + dy, Color13);
if ( dy = 0 ) then exit;
PutPixel (cx + dx, cy - dy, Color13);
End;
PutPixel (cx - dx, cy - dy, Color13);
End;