填充矩形

发布于 2024-11-03 01:09:38 字数 158 浏览 1 评论 0原文

我创建了一个 winform 程序,它使用 Graphics 对象和 2 个 for 循环来根据用户输入生成方形网格。

我还创建了一种方法,通过使用与网格相同的坐标,用随机颜色填充网格中的每个方块。

现在我想通过使用光标位置单击每个方块来独立绘制每个方块。我应该怎么做?

Ive created a winform-program that uses a Graphics object and 2 for-loops to generate a square grid, depending on user input.

I also created a method that fills each square in the grid with a random color by using the same coordinates as the grid.

Now I want to paint each square independently by clicking on it, using the cursor position. How should i do this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

可爱咩 2024-11-10 01:09:38

为什么不跟踪网格线,从而知道您在哪个方块内单击了呢?根据这些知识,你可以在一个人所属的地方画出一个正方形。

Why don't you just keep track of the gridlines and thus know what square you clicked within? From this knowledge you could paint a square where one belongs.

落花浅忆 2024-11-10 01:09:38

洪水填充是最简单的。与其他方法相比,它的速度较慢,并且会占用堆栈空间,但对于使用时间不到 15 年的计算机来说,这应该不成问题。

更新

正如@Ron提到的,典型的递归洪水填充很容易破坏堆栈。因此,我修改了代码以使用 Stack<> 实例(我相信它是从堆分配的)和所谓的“数据递归”。对于大(2000x2000+像素)区域来说它仍然相当慢,但对于小区域来说应该没问题。

bool[] canDraw;
/// <summary>
/// make sure that the given point is within our image boundaries.
/// BufferSize(Point) contains the dimensions of the image buffer.
/// </summary>
/// <param name="p"></param>
/// <returns></returns>
bool InBounds(Point p)
{
    return p.X >= 0 && p.X < BufferSize.X && p.Y >= 0 && p.Y < BufferSize.Y;
}

/// <summary>
/// make sure that we haven't already drawn this pixel and that it has
/// valid coordinates
/// </summary>
/// <param name="p"></param>
/// <returns></returns>
bool CanDraw(Point p)
{
    return InBounds(p) && canDraw[p.Y * BufferSize.X + p.X];
}

/// <summary>
/// Heap "stack" to track which pixels we need to visit
/// </summary>
Stack<Point> fillStack = new Stack<Point>();

/// <summary>
/// initialize recursion.
/// </summary>
/// <param name="startPosition"></param>
/// <param name="fillColor"></param>
void Fill(Point startPosition, Color fillColor)
{
    canDraw = Enumerable.Repeat(true, BufferSize.X * BufferSize.Y).ToArray();
    var backgroundColor = GetPixel(startPosition);

    if (backgroundColor != fillColor)
    {
        fillStack.Push(startPosition);
        RecurseFloodFill(fillColor, backgroundColor);
    }

}

/// <summary>
/// data-recurse through the image.
/// </summary>
/// <param name="fillColor">Color we want to fill with</param>
/// <param name="backgroundColor">Initial background color to overwrite</param>
void RecurseFloodFill(Color fillColor, Color backgroundColor)
{
    while (fillStack.Count > 0 && !IsExiting)
    {
        /*if (fillStack.Count != depth)
            Debug.WriteLine("Depth: {0}", depth = fillStack.Count);
        */
        var position = fillStack.Pop();
        if(!CanDraw(position))
            continue;

        var color = GetPixel(position);
        if (color != backgroundColor)
            continue;

        SetPixel(position, fillColor);

        for(var i=position.X-1;i<=position.X+1;i++)
            for (var j = position.Y - 1; j <= position.Y + 1; j++)
            {
                var p = new Point(i, j);
                fillStack.Push(p);
            }

    }

}

注意我什至没有尝试编译它。

基本上:

  1. 单击时:读取用户单击的颜色(背景颜色)。
  2. 递归开始:
  3. 读取用户点击处的颜色(背景色)。
  4. 确保它与步骤 1 中的背景颜色相同。否则返回。
  5. 将像素设置为填充颜色。
  6. 对于 8 个周围像素,递归到步骤 3。当您读取的背景像素与最初读取的背景颜色不同时,递归停止。

A flood fill is easiest. It's slow compared to other methods and eats stack space, but it shouldn't be a problem on a computer that's less than 15 years old.

Update

As @Ron mentioned, a typical recursive floodfill blows the stack pretty easily. So, I modified the code to use a Stack<> instance (which I believe is allocated from the heap) and so-called "data recursion". It's still pretty slow for large (2000x2000+ pixel) areas, but should be just fine for small ones.

bool[] canDraw;
/// <summary>
/// make sure that the given point is within our image boundaries.
/// BufferSize(Point) contains the dimensions of the image buffer.
/// </summary>
/// <param name="p"></param>
/// <returns></returns>
bool InBounds(Point p)
{
    return p.X >= 0 && p.X < BufferSize.X && p.Y >= 0 && p.Y < BufferSize.Y;
}

/// <summary>
/// make sure that we haven't already drawn this pixel and that it has
/// valid coordinates
/// </summary>
/// <param name="p"></param>
/// <returns></returns>
bool CanDraw(Point p)
{
    return InBounds(p) && canDraw[p.Y * BufferSize.X + p.X];
}

/// <summary>
/// Heap "stack" to track which pixels we need to visit
/// </summary>
Stack<Point> fillStack = new Stack<Point>();

/// <summary>
/// initialize recursion.
/// </summary>
/// <param name="startPosition"></param>
/// <param name="fillColor"></param>
void Fill(Point startPosition, Color fillColor)
{
    canDraw = Enumerable.Repeat(true, BufferSize.X * BufferSize.Y).ToArray();
    var backgroundColor = GetPixel(startPosition);

    if (backgroundColor != fillColor)
    {
        fillStack.Push(startPosition);
        RecurseFloodFill(fillColor, backgroundColor);
    }

}

/// <summary>
/// data-recurse through the image.
/// </summary>
/// <param name="fillColor">Color we want to fill with</param>
/// <param name="backgroundColor">Initial background color to overwrite</param>
void RecurseFloodFill(Color fillColor, Color backgroundColor)
{
    while (fillStack.Count > 0 && !IsExiting)
    {
        /*if (fillStack.Count != depth)
            Debug.WriteLine("Depth: {0}", depth = fillStack.Count);
        */
        var position = fillStack.Pop();
        if(!CanDraw(position))
            continue;

        var color = GetPixel(position);
        if (color != backgroundColor)
            continue;

        SetPixel(position, fillColor);

        for(var i=position.X-1;i<=position.X+1;i++)
            for (var j = position.Y - 1; j <= position.Y + 1; j++)
            {
                var p = new Point(i, j);
                fillStack.Push(p);
            }

    }

}

Note that I haven't even tried to compile this.

Basically:

  1. On click: read the color (background color) where the user clicked.
  2. Recursion start:
  3. read the color (background color) where the user clicked.
  4. Make sure it's the same as the background color from step 1. Else return.
  5. Set the pixel to the fill color.
  6. Recurse to step 3 for the 8 surrounding pixels. Recursion stops when you read a background pixel that's different than the initially-read background color.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文