Увеличьте масштаб до середины панели в C# Winforms


У меня есть приложение, которое я могу увеличивать и уменьшать масштаб, только проблема я хочу увеличивать и уменьшать масштаб, но сфокусирован на текущей середине панели, вместо того, чтобы использовать положение мыши

Есть идеи?

private void panel1_Paint(object sender, PaintEventArgs e)
{
    SolidBrush brushs = new SolidBrush(Color.White);
    e.Graphics.Clip = new Region(new Rectangle(0, 0, Viewer.Width, Viewer.Height));
    e.Graphics.FillRegion(brushs, e.Graphics.Clip);

    Graphics g = e.Graphics;
    g.TranslateTransform(_ImgX, _ImgY);
    g.ScaleTransform(_Zoom, _Zoom);
    g.SmoothingMode = SmoothingMode.AntiAlias;
    SolidBrush myBrush = new SolidBrush(Color.Black);
    Pen p = new Pen(Color.Red);
    foreach (CircuitData.ResistorRow resistorRow in ResistorData.Resistor)
    {
        RectangleF rec = new RectangleF((float)(resistorRow.CenterX  - resistorRow.Length/ 2), (float)(resistorRow.CenterY - resistorRow.Width/ 2), (float)resistorRow.Length, (float)resistorRow.Width);
        float orientation = 360 - (float)resistorRow.Orientation;
        PointF center = new PointF((float)resistorRow.CenterX, (float)resistorRow.CenterY);
        PointF[] points = CreatePolygon(rec, center, orientation);
        if (!Double.IsNaN(resistorRow.HiX) && !Double.IsNaN(resistorRow.HiY))
        {
            g.FillEllipse(myBrush, (float)resistorRow.HiX  - 2 , (float)resistorRow.HiY - 2, 4, 4);
            g.DrawLine(p, new PointF((float)resistorRow.HiX , (float)resistorRow.HiY ), center);
    }

    g.FillPolygon(myBrush, points);
}

}

Обновление Функция масштабирования, я знаю, что это неправильно, но если кто-нибудь может помочь мне исправить логику или лучшую идею.

    private void trackBar1_Scroll(object sender, EventArgs e)
    {
        float oldZoom = _Zoom;
        _Zoom = zoomTrackPad.Value / 10f;

        int x = Math.Abs(Viewer.Width / 2 );
        int y = Math.Abs(Viewer.Height / 2 );

        int oldImageX = (int)(x / oldZoom);
        int oldImageY = (int)(y / oldZoom);

        int newImageX = (int)(x / _Zoom);
        int newImageY = (int)(y / _Zoom);

        _ImgX = newImageX - oldImageX + _ImgX;
        _ImgY = newImageY - oldImageY + _ImgY;

        Viewer.Invalidate();
    }

Спасибо

1 4

1 ответ:

Как вы сказали, ваша логика масштабирования неверна. И это неправильно из-за того, как работают матрицы преобразования.

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

Я собираюсь предположить некоторые вещи здесь, если что-то не так комментарий пеласе.

Предположим, что ваша видимая область (размер панели) составляет 100 * 100 пикселей, и вы требуется "виртуальный" контент размером 200 * 200 пикселей. Я думаю, что у вас есть две полосы прокрутки для перемещения содержимого внутри панели, если вы просто используете размер панели и не имеете полос прокрутки, то вам следует адаптировать способ расчета вашего перемещения.

Таким образом, ваши полосы прокрутки будут варьироваться от 0 до (панель.(с / высота) - содержание.(ширина / высота)), в этом случае будет колебаться от 0 до 100.

Давайте сначала сделаем наш контент центрированным в начале. Чтобы центрировать его, мы применяем TranslateTransform к Графический объект с половиной размера нашей видимой области меньше половины размера виртуальной области:

e.Graphics.TranslateTransform(panel.With / 2 - content.Width / 2, panel.Height / 2 - content.Height / 2);

Это сделает рисунок центрированным, но, эй! мы не используем полосы прокрутки позиционирования!

Окококо, давай применим эти смещения полосы прокрутки. Во-первых, поскольку наш контент центрирован, установите значения этих полос прокрутки на половину его максимального значения, в нашем примере мы должны установить их на 50.

Теперь, как мы сказали, преобразования добавляются, так что ничего мешает вам добавить новую TranslateTransform. Вы можете сделать это только за один вызов, но для простоты мы делаем это в два вызова (кроме того, это будет лучше для применения масштабирования позже).

e.Graphics.TranslateTransform(panel.With / 2 - content.Width / 2, panel.Height / 2 - content.Height / 2);
e.Graphics.TranslateTransform(hScrollBar1.Value - (hScrollbar1.Maximum / 2), vScrollBar1.Value - (vScrollbar1.Maximum / 2));

Как вы видите, мы вычитаем половину его максимального значения, у нас уже есть наше содержимое по центру, поэтому, когда полосы прокрутки находятся в центре, нам нужно применить перевод 0, мы будем варьироваться от-Maximum / 2 до Maximum / 2, в этом примере от -50 до 50.

Итак, теперь у нас есть виртуальная область контента по центру нашего экрана, и мы можем перемещать на нем, теперь нам нужно применить этот зум.

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

Диапазон полосы прокрутки будет вашим выбором, я вижу, что вы используете значение / 10f, поэтому, если ваша полоса прокрутки колеблется от 1 до 100, у вас будет масштаб от 0,1 до 10, это хороший выбор для начала.

Теперь давайте установим эту полосу прокрутки на 10,чтобы изначально иметь унитарный масштаб.

Теперь начинается интересная часть, преобразования применяются в том порядке, в котором вы их задаете, поэтому применять масштабирование перед переводом не то же самое, что делать это после.

В нашем случае мы сначала хотим центрировать содержимое, перевести его в положение, которое мы хотим видеть (значения полос прокрутки), а затем увеличить.

e.Graphics.TranslateTransform(panel.With / 2 - content.Width / 2, panel.Height / 2 - content.Height / 2);
e.Graphics.TranslateTransform(hScrollBar1.Value - (hScrollbar1.Maximum / 2), vScrollBar1.Value - (vScrollbar1.Maximum / 2));
float scale = zScrollBar.Value / 10f;
e.Graphics.ScaleTransform(scale, scale);
Хорошо, звучит неплохо, но подождите! он потерпит полный крах. Почему? потому что при масштабировании наш виртуальный размер также масштабируется относительно области просмотра (размер панели), поэтому нам нужно применить эта шкала соответствует размеру содержимого (и, следовательно, значениям полосы прокрутки):
float scale = zScrollBar.Value / 10f;
e.Graphics.TranslateTransform(panel.With / 2 - ((content.Width / 2) * scale), panel.Height / 2 - ((content.Height / 2) * scale));
e.Graphics.TranslateTransform((hScrollBar1.Value - (hScrollbar1.Maximum / 2)) * scale, (vScrollBar1.Value - (vScrollbar1.Maximum / 2)) * scale);
e.Graphics.ScaleTransform(scale, scale);

И, наконец, у нас есть полностью работающая виртуальная область, где вы можете увеличивать и перемещать ее границы.

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

Ура!

P. D.: Если вы не хотите иметь возможность перемещать виртуальную область и просто увеличивать масштаб по центру панели, пропустите вторую кроме того, если вы хотите увеличить текущий видимый центр виртуальной области, просто используйте смещение (без масштабирования, преобразования уже имеют логику для этого) вместо значений полос прокрутки.