Заполнение прямоугольников

#c# #graphics #grid #draw

#c# #графика #сетка #рисовать

Вопрос:

Я создал winform-программу, которая использует графический объект и 2 цикла for для генерации квадратной сетки, в зависимости от пользовательского ввода.

Я также создал метод, который заливает каждый квадрат в сетке случайным цветом, используя те же координаты, что и сетка.

Теперь я хочу нарисовать каждый квадрат независимо, щелкнув по нему, используя положение курсора. Как мне это сделать?

Комментарии:

1. С какой частью у вас возникли проблемы?

Ответ №1:

Почему бы вам просто не отслеживать линии сетки и, таким образом, знать, в каком квадрате вы щелкнули? Исходя из этих знаний, вы могли бы нарисовать квадрат там, где ему место.

Комментарии:

1. простые, удобные в реализации ответы вредят безопасности работы. 1

2. Кроме того, если работа выполняется слишком быстро, вы не сможете выставить клиенту такой большой счет 🙂

3. Лол, сомневаюсь в этом. StackOverflow не содержит ничего интересного для простых смертных.

4. Смехотворно! xP, но как я могу отслеживать линии сетки?

5. Цитирую «себя»: «Я создал winform-программу, которая использует графический объект и 2 цикла for для генерации квадратной сетки в зависимости от пользовательского ввода». Если вы рисуете сетку, вы должны знать, где вы проводите линии: p

Ответ №2:

Заливка потоком проще всего. Это медленно по сравнению с другими методами и занимает место в стеке, но это не должно быть проблемой на компьютере, которому меньше 15 лет.

Обновить

Как упоминал @Ron, типичное рекурсивное заполнение довольно легко разрушает стек. Итак, я изменил код, чтобы использовать Stack<> экземпляр (который, как я полагаю, выделен из кучи) и так называемую «рекурсию данных». Это все еще довольно медленно для больших областей (2000×2000 пикселей), но должно быть просто отлично для маленьких.

 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 amp;amp; p.X < BufferSize.X amp;amp; p.Y >= 0 amp;amp; 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) amp;amp; 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 amp;amp; !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. Вернитесь к шагу 3 для 8 окружающих пикселей. Рекурсия останавливается, когда вы считываете пиксель фона, который отличается от цвета фона, который был прочитан изначально.

Комментарии:

1. Я не уверен, что это то, о чем он просил. Кроме того, рекурсивное заполнение потоком почти наверняка приведет к нехватке места в стеке даже для небольших изображений.

2. @Ron изменил его, чтобы использовать кучу. Теперь работает просто отлично.