在C#中将填充和未填充椭圆的点渲染为数组

时间:2012-06-23 02:20:20

标签: c# math drawing ellipse

有没有人知道在C#中将Ellipse渲染到数组的任何代码?我看了一下,找不到能解决问题的任何东西。

给出以下数组:

bool[,] pixels = new bool[100, 100];

我正在寻找在矩形区域内渲染空心和填充椭圆的函数。 e.g:

public void Ellipse(bool[,] pixels, Rectangle area)
{
    // fill pixels[x,y] = true here for the ellipse within area.
}

public void FillEllipse(bool[,] pixels, Rectangle area)
{
    // fill pixels[x,y] = true here for the ellipse within area.
}

Ellipse(pixels, new Rectangle(20, 20, 60, 60));
FillEllipse(pixels, new Rectangle(40, 40, 20, 20));

非常感谢任何帮助。

4 个答案:

答案 0 :(得分:2)

这样的事情可以解决问题

public class EllipseDrawer
{
    private static PointF GetEllipsePointFromX(float x, float a, float b)
    {
        //(x/a)^2 + (y/b)^2 = 1
        //(y/b)^2 = 1 - (x/a)^2
        //y/b = -sqrt(1 - (x/a)^2)  --Neg root for upper portion of the plane
        //y = b*-sqrt(1 - (x/a)^2)
        return new PointF(x, b * -(float)Math.Sqrt(1 - (x * x / a / a)));
    }

    public static void Ellipse(bool[,] pixels, Rectangle area)
    {
        DrawEllipse(pixels, area, false);
    }

    public static void FillEllipse(bool[,] pixels, Rectangle area)
    {
        DrawEllipse(pixels, area, true);
    }

    private static void DrawEllipse(bool[,] pixels, Rectangle area, bool fill)
    {
        // Get the size of the matrix
        var matrixWidth = pixels.GetLength(0);
        var matrixHeight = pixels.GetLength(1);

        var offsetY = area.Top;
        var offsetX = area.Left;

        // Figure out how big the ellipse is
        var ellipseWidth = (float)area.Width;
        var ellipseHeight = (float)area.Height;

        // Figure out the radiuses of the ellipses
        var radiusX = ellipseWidth / 2;
        var radiusY = ellipseHeight / 2;

        //Keep track of the previous y position
        var prevY = 0;
        var firstRun = true;

        // Loop through the points in the matrix
        for (var x = 0; x <= radiusX; ++x)
        {
            var xPos = x + offsetX;
            var rxPos = (int)ellipseWidth - x - 1 + offsetX;

            if (xPos < 0 || rxPos < xPos || xPos >= matrixWidth)
            {
                continue;
            }

            var pointOnEllipseBoundCorrespondingToXMatrixPosition = GetEllipsePointFromX(x - radiusX, radiusX, radiusY);
            var y = (int) Math.Floor(pointOnEllipseBoundCorrespondingToXMatrixPosition.Y + (int)radiusY);
            var yPos = y + offsetY;

            var ryPos = (int)ellipseHeight - y - 1 + offsetY;

            if (yPos >= 0)
            {
                if (xPos > -1 && xPos < matrixWidth && yPos > -1 && yPos < matrixHeight)
                {
                    pixels[xPos, yPos] = true;
                }

                if(xPos > -1 && xPos < matrixWidth && ryPos > -1 && ryPos < matrixHeight)
                {
                    pixels[xPos, ryPos] = true;
                }

                if (rxPos > -1 && rxPos < matrixWidth)
                {
                    if (yPos > -1 && yPos < matrixHeight)
                    {
                        pixels[rxPos, yPos] = true;
                    }

                    if (ryPos > -1 && ryPos < matrixHeight)
                    {
                        pixels[rxPos, ryPos] = true;
                    }
                }
            }

            //While there's a >1 jump in y, fill in the gap (assumes that this is not the first time we've tracked y, x != 0)
            for (var j = prevY - 1; !firstRun && j > y - 1 && y > 0; --j)
            {
                var jPos = j + offsetY;
                var rjPos = (int)ellipseHeight - j - 1 + offsetY;

                if(jPos == rjPos - 1)
                {
                    continue;
                }

                if(jPos > -1 && jPos < matrixHeight)
                {
                    pixels[xPos, jPos] = true;
                }

                if(rjPos > -1 && rjPos < matrixHeight)
                {
                    pixels[xPos, rjPos] = true;
                }

                if (rxPos > -1 && rxPos < matrixWidth)
                {
                    if(jPos > -1 && jPos < matrixHeight)
                    {
                        pixels[rxPos, jPos] = true;
                    }

                    if(rjPos > -1 && rjPos < matrixHeight)
                    {
                        pixels[rxPos, rjPos] = true;
                    }
                }
            }

            firstRun = false;
            prevY = y;
            var countTarget = radiusY - y;

            for (var count = 0; fill && count < countTarget; ++count)
            {
                ++yPos;
                --ryPos;

                // Set all four points in the matrix we just learned about
                //  also, make the indication that for the rest of this row, we need to fill the body of the ellipse
                if(yPos > -1 && yPos < matrixHeight)
                {
                    pixels[xPos, yPos] = true;
                }

                if(ryPos > -1 && ryPos < matrixHeight)
                {
                    pixels[xPos, ryPos] = true;
                }

                if (rxPos > -1 && rxPos < matrixWidth)
                {
                    if(yPos > -1 && yPos < matrixHeight)
                    {
                        pixels[rxPos, yPos] = true;
                    }

                    if(ryPos > -1 && ryPos < matrixHeight)
                    {
                        pixels[rxPos, ryPos] = true;
                    }
                }
            }
        }
    }
}

答案 1 :(得分:2)

虽然似乎已经有perfectly valid answer的源代码和所有这个问题,但我只想指出WriteableBitmapEx项目还包含很多高效的source code在所谓的WriteableBitmap对象中绘制和填充不同的多边形类型(如椭圆)。

此代码可以很容易地适应一般场景,其中2D阵列(或2D阵列的1D表示)应以不同方式呈现。

对于椭圆情况,请特别注意 WriteableBitmapShapeExtensions.cs 文件中的DrawEllipse...方法和 WriteableBitmapFillExtensions.cs中的FillEllipse...方法文件,一切都位于 trunk / Source / WriteableBitmapEx 子文件夹中。

答案 2 :(得分:1)

这更适用于所有语言,而且我不确定为什么你会特别寻找这样的东西,而不是使用预先存在的图形库(作业?),而是用于绘图一个椭圆,我建议使用中点线绘制算法,它可以适应椭圆(也是一个圆):

http://en.wikipedia.org/wiki/Midpoint_circle_algorithm

我不确定我是否完全同意它是Bresenham算法的概括(当然我们被告知Bresenham&Midpoint算法不同但被证明产生相同的结果),但该页面应该为您提供一个开始。有关省略号的算法,请参阅底部附近纸张的链接。

至于填充椭圆,我说你最好的选择是采用扫描线方法 - 依次查看每一行,找出左右两侧的线条所在的像素,然后填写每一行像素中间。

答案 3 :(得分:0)

最简单的做法是迭代矩阵的每个元素,并检查一些椭圆方程是否计算为真

enter image description here

取自http://en.wikipedia.org/wiki/Ellipse

我开始的是类似于

的东西
        bool[,] pixels = new bool[100, 100];

        double a = 30;
        double b = 20;
        for (int i = 0; i < 100; i++)
            for (int j = 0; j < 100; j++ )
            {
                double x = i-50;
                double y = j-50;
                pixels[i, j] = (x / a) * (x / a) + (y / b) * (y / b) > 1;
            }

如果您的elipse正好相反,那么只需将>更改为<

对于空心的,您可以检查(x / a) * (x / a) + (y / b) * (y / b)1之间的差异是否在特定阈值之内。如果你只是将不等式改为等式,它可能会遗漏一些像素。

现在,我还没有完全测试过,所以我不知道方程是否正确应用,但我只想说明这个概念。