Как перерисовать PictureBox с помощью TrackBar Value, чтобы повлиять на рисунок - PullRequest
1 голос
/ 07 марта 2020

Я рисовал разные фракталы, и мне нужно было перерисовать фрактал при изменении значения некоторых трекбаров.
В этом случае значение изменяет глубину рекурсии фрактальной функции.
Моя проблема в том, что после того, как мой фрактал сделан, он исчезает.

Я пытался использовать:

pictureBox1.Invalidate();
DrawCloverFractal(...);    //fractal drawing method

, но я не мог найти способ заставить его работать должным образом.

Я также пытался использовать:

if (pictureBox1.InitialImage != null)
{
    pictureBox1.InitialImage.Dispose();
    pictureBox1.InitialImage = null;
    pictureBox1.Invalidate(); 
}

, но он вообще ничего не рисовал.

Полный код:

//Drawing method
int DrawCloverFractal(int x0, int y0, int r, int dir, int iter)

{
    var g = pictureBox1.CreateGraphics();
    var S = new SolidBrush(Color.Black);

    g.FillEllipse(S, x0 - r, y0 - r, 2 * r, 2 * r);

    if (iter == 0)
        return 0;
    int[] x = new int[4];
    int[] y = new int[4];
    int d = 3 * r / 2;
    x[0] = x0 - d;
    y[0] = y0;
    x[1] = x0;
    y[1] = y0 - d;
    x[2] = x0 + d;
    y[2] = y0;
    x[3] = x0;
    y[3] = y0 + d;

    for (int i = 0; i < 4; i++)
    {
        if (i - dir == 2 || i - dir == -2)
            continue;
        DrawCloverFractal(x[i], y[i], r / 2, i, iter - 1);
    }
    return 0;
}

//Scroll method where I call DrawCloverFractal
private void RecursionLevel_Scroll(object sender, EventArgs e)
{
    pictureBox1.Invalidate();
    DrawCloverFractal(pictureBox1.Width / 2, pictureBox1.Height / 2, (int)(pictureBox1.Height * 0.17), 1, RecursionLevel.Value);
}

1 Ответ

1 голос
/ 07 марта 2020

Есть несколько проблем с текущим кодом:

  1. Вы используете Control.CreateGraphics () .
    Графический объект, созданный с помощью этого метода, не является постоянным, он создается заново каждый раз, когда элемент управления должен быть перекрашен; событие, которое происходит довольно часто. Если вы рисуете на поверхности элемента управления с помощью этого объекта, рисунок не будет сохраняться: графический объект, который описывает контекст устройства элемента управления, не будет таким же.

    См. Также:
    Начало работы с графическим программированием
    Как создать графические объекты для рисования

  2. Целочисленные значения используются для создания чертежа .
    Почти все графические меры должны быть выражены значениями с плавающей запятой. Округление, вызванное целочисленным делением, ставит под угрозу вычисления. Не имеет значения, требует ли конечная функция Graphics - иногда - целочисленные аргументы: это конечное значение, часто используемое в этой форме, потому что внутренние методы - или объекты Graphics - не поддерживают меры с плавающей запятой (касающиеся положения пикселей является одной из причин).

    См., Например, это: Поворот точки вокруг точки поворота несколько раз

В WinForms Graphics, когда рисунок должен сохраняться на поверхности элемента управления, Событие рисования (или переопределение метода OnPaint ) предоставляет объект Graphics, используемый для рисования.

В этом примере PaintEventArgs события PictureBox Paint предоставляет этот объект Graphics, а метод DrawCloverFractal() затем использует его для рисования фрактала.

Событие ValueChanged элемента управления TrackBar обеспечивает уровень рекурсии, который используется функцией для создания вложенных поколений.

Метод, генерирующий фрактал, был перемещен в объект класса, который предоставляет c методы для настройки фрактала:

  • Control ( Холст), который будет обеспечивать поверхность рисования
  • Направление рисования
  • Количество итераций
  • Цвет Бру sh

► Метод DrawCloverFractal() принимает объект Graphics в качестве аргумента. Этот объект передается методу, когда генерируется событие Paint Canvas.

Мы можем заставить Control перерисовывать себя, когда нам это нужно, вызывая его метод Invalidate () (который обновляет элемент управления асинхронным способом ).

► Затем метод publi c DrawCloverFractal() вызывает внутренний DrawCloverFractalinternal(), передавая рекурсивному методу необходимый параметр, используя значения, присвоенные свойства класса и генерация других значений, которые будут постоянными в текущей рекурсии, как центр холста.

► При перетаскивании ручки элемента управления TrackBar возникает событие ValueChanged. Обработчику событий просто нужно установить свойство Iterations объекта класса в текущее значение Value, а затем вызвать pictureBox1.Invalidate(), чтобы переопределить sh PictureBox, используемый в качестве Canvas, который в свою очередь вызывает метод DrawCloverFractal(), передавая fre sh Графический объект:

public partial class Form1 : Form
{
    private MyFractal fractalDrawing = null;

    public Form1()
    {
        InitializeComponent();
        fractalDrawing = new MyFractal(this.pictureBox1);
    }

    private void trkRecursionLevel_ValueChanged(object sender, EventArgs e)
    {
        fractalDrawing.Iterations = (sender as TrackBar).Value;
        pictureBox1.Invalidate();
    }

    private void pictureBox1_Paint(object sender, PaintEventArgs e)
    {
        if (fractalDrawing == null) return;
        fractalDrawing.DrawCloverFractal(e.Graphics);
    }
}

Визуальный образец результатов:

Fractal drawing sample

Объект класса, который содержит все методы рисования и логика c:

public class MyFractal
{
    public MyFractal() : this(null) { }
    public MyFractal(Control canvas) => this.Canvas = canvas;

    public Control Canvas { get; set; }
    public Color Color { get; set; } = Color.LightGreen;
    public int Direction { get; set; } = 1;
    public int Iterations { get; set; } = 1;

    public void DrawCloverFractal(Graphics g)
    {
        if (this.Canvas == null) return;
        PointF center = new PointF(this.Canvas.Width / 2.0f, this.Canvas.Height / 2.0f);
        float r = this.Canvas.Height * .17f;
        DrawCloverFractalinternal(g, center, r, this.Direction, this.Iterations);
    }

    internal void DrawCloverFractalinternal(Graphics g, PointF center, float r, int dir, int iter)
    {
        g.SmoothingMode = SmoothingMode.AntiAlias;
        using (var brush = new SolidBrush(this.Color)) {
            g.FillEllipse(brush, center.X - r, center.Y - r, 2 * r, 2 * r);
        }

        if (iter == 0) return;
        float[] x = new float[4];
        float[] y = new float[4];
        float d = 3 * r / 2;
        x[0] = center.X - d;
        y[0] = center.Y;
        x[1] = center.X;
        y[1] = center.Y - d;
        x[2] = center.X + d;
        y[2] = center.Y;
        x[3] = center.X;
        y[3] = center.Y + d;

        for (int i = 0; i < 4; i++) {
            if (i - dir == 2 || i - dir == -2) continue;
            DrawCloverFractalinternal(g, new PointF(x[i], y[i]), r / 2, i, iter - 1);
        }
    }
}
...