2

有谁知道将椭圆渲染到 C# 中的数组的任何代码?我看了看,我找不到任何可以回答我的问题的东西。

给定以下数组:

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

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

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

4 回答 4

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;
                    }
                }
            }
        }
    }
}
于 2012-06-28T09:29:59.227 回答
2

尽管似乎已经有了源代码和这个问题的完全有效的答案,但我只想指出,WriteableBitmapEx项目还包含许多有效的源代码,用于绘制和填充不同的多边形类型(例如椭圆)所谓的WriteableBitmap对象。

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

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

于 2012-06-29T11:58:46.030 回答
1

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

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

我不确定我是否完全同意这是对 Bresenham 算法的概括(当然,我们被告知 Bresenham 和 Midpoint 算法不同,但被证明产生相同的结果),但该页面应该让您开始了解它。有关特定于椭圆的算法,请参阅底部附近的论文链接。

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

于 2012-06-23T02:28:29.833 回答
0

最简单的做法是遍历矩阵的每个元素,并检查某个椭圆方程的计算结果是否为真

在此处输入图像描述

取自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;
            }

如果你的椭圆是相反的,不仅仅是>改变<

(x / a) * (x / a) + (y / b) * (y / b)对于空心的,您可以检查和之间的差异是否1在某个阈值之内。如果您只是将不等式更改为方程式,它可能会丢失一些像素。

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

于 2012-06-27T21:46:34.003 回答