🎨 Графическое программирование на C#

← Вернуться к модулю 4

Урок 4.4: Трансформации (поворот, масштаб)

Модуль 4 ⏱️ ~2 часа

📌 Ключевые моменты этого урока:

  • 3 основных трансформации: Translate (сдвиг), Rotate (поворот), Scale (масштаб)
  • Порядок имеет значение! Translate → Rotate даёт другой результат чем Rotate → Translate
  • Для поворота вокруг центра фигуры: Translate(centerX, centerY) → Rotate → Translate(-centerX, -centerY)
  • g.Save()/g.Restore() сохраняют и восстанавливают полное состояние Graphics (трансформация + стиль рисования)
  • Трансформации кумулятивны - каждая новая применяется к результату предыдущей

Matrix трансформации

ПОЧЕМУ это важно: Трансформации позволяют поворачивать, масштабировать, двигать объекты без пересчёта каждого пикселя вручную.

Matrix позволяет применять различные трансформации к графике:

using System.Drawing.Drawing2D;

Graphics g = e.Graphics;
Matrix matrix = new Matrix();

// Применение трансформаций
g.Transform = matrix;

RotateTransform

// Поворот на 45 градусов вокруг начала координат
g.RotateTransform(45);

// Поворот вокруг определенной точки
g.TranslateTransform(100, 100); // Переместить центр
g.RotateTransform(45);
g.TranslateTransform(-100, -100); // Вернуть обратно
g.DrawRectangle(Pens.Black, 50, 50, 100, 50);

ScaleTransform

// Масштабирование
g.ScaleTransform(2, 2); // Увеличение в 2 раза
g.DrawRectangle(Pens.Black, 10, 10, 50, 50);

// Разное масштабирование по осям
g.ScaleTransform(2, 1); // Удвоить по X, оставить Y

// Сохранение и восстановление состояния
GraphicsState state = g.Save();
g.ScaleTransform(2, 2);
// Рисование
g.Restore(state);

TranslateTransform

// Сдвиг
g.TranslateTransform(100, 50); // Сдвиг на 100 по X, 50 по Y
g.DrawRectangle(Pens.Black, 0, 0, 50, 50); // Нарисуется в (100, 50)

// Комбинация трансформаций
g.ResetTransform();
g.TranslateTransform(200, 200); // Переместить центр
g.RotateTransform(45);            // Повернуть
g.ScaleTransform(1.5f, 1.5f);     // Увеличить
g.DrawRectangle(Pens.Black, -25, -25, 50, 50);

📝 Пример задания — поворот изображения (Task11)

Загружает изображение и рисует оригинал рядом с его копией, повёрнутой на 45° вокруг центра:

using System;
using System.Drawing;
using System.Windows.Forms;

namespace CSharpGraphicsApp
{
    public class Task11_ImageRotate : Form
    {
        private Image img;

        public Task11_ImageRotate()
        {
            Text = "Задание 11 — Поворот изображения";
            Size = new Size(500, 450);
            BackColor = Color.White;
            DoubleBuffered = true;
            Paint += Form1_Paint;
            Load += (s, e) =>
            {
                try   { img = Image.FromFile("image.jpg"); }
                catch { img = GenerateTestImage(150, 100); }
            };
        }

        private void Form1_Paint(object sender, PaintEventArgs e)
        {
            if (img == null) return;
            Graphics g = e.Graphics;

            g.DrawImage(img, 20, 40);
            g.DrawString("Оригинал", SystemFonts.DefaultFont,
                Brushes.Black, 20, 20);

            g.TranslateTransform(300, 150);
            g.RotateTransform(45);
            g.DrawImage(img, -img.Width / 2, -img.Height / 2);
            g.ResetTransform();
            g.DrawString("Rotate 45°", SystemFonts.DefaultFont,
                Brushes.Black, 260, 20);
        }

        private static Bitmap GenerateTestImage(int w, int h)
        {
            Bitmap bmp = new Bitmap(w, h);
            using (Graphics g = Graphics.FromImage(bmp))
            {
                g.Clear(Color.LightGreen);
                g.DrawString("IMG",
                    new Font("Arial", 24, FontStyle.Bold), Brushes.DarkGreen, 20, 20);
            }
            return bmp;
        }

        protected override void OnFormClosed(FormClosedEventArgs e)
        {
            img?.Dispose();
            base.OnFormClosed(e);
        }
    }
}

📝 Пример задания — анимированные трансформации (Task17)

Таймер обновляет угол поворота и масштаб через Math.Sin, создавая пульсирующий вращающийся прямоугольник:

using System;
using System.Drawing;
using System.Windows.Forms;

namespace CSharpGraphicsApp
{
    public class Task17_Transformations : Form
    {
        private float angle = 0;
        private float scale = 1f;
        private readonly Timer timer = new Timer();

        public Task17_Transformations()
        {
            Text = "Задание 17 — Трансформации";
            Size = new Size(500, 400);
            BackColor = Color.White;
            DoubleBuffered = true;
            Paint += Form1_Paint;

            timer.Interval = 50;
            timer.Tick += Timer_Tick;
            timer.Start();
            FormClosed += (s, e) => timer.Stop();
        }

        private void Timer_Tick(object sender, EventArgs e)
        {
            angle += 3;
            scale = 1 + 0.3f * (float)Math.Sin(angle * Math.PI / 180);
            Invalidate();
        }

        private void Form1_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

            g.TranslateTransform(200, 180);
            g.RotateTransform(angle);
            g.ScaleTransform(scale, scale);

            using (var brush = new SolidBrush(Color.FromArgb(100, Color.Green)))
                g.FillRectangle(brush, -40, -30, 80, 60);
            using (var pen = new Pen(Color.DarkGreen, 2))
                g.DrawRectangle(pen, -40, -30, 80, 60);

            g.ResetTransform();
            g.DrawString("Поворот + Масштабирование",
                SystemFonts.DefaultFont, Brushes.Black, 10, 10);
        }
    }
}

Практическое задание

Задание: Трансформации

  1. Создайте анимацию вращающегося объекта
  2. Добавьте возможность изменения скорости вращения
  3. Реализуйте масштабирование объекта

Задание по аналогии: Поворот изображения (Task11)

По аналогии с Task11_ImageRotate создайте форму, демонстрирующую поворот изображения через трансформации Graphics.

  1. Загрузите изображение в событии Load (при ошибке создайте программно).
  2. В Paint нарисуйте оригинал слева: g.DrawImage(img, 20, 40).
  3. Для повёрнутой копии: переместите начало координат к центру цели (TranslateTransform), поверните (RotateTransform(45)), нарисуйте со смещением (-img.Width/2, -img.Height/2).
  4. Сбросьте трансформации через g.ResetTransform().
  5. Дополнительно: добавьте кнопки «+15°» / «-15°» для изменения угла.
💡 Совет: Используйте g.Save() / g.Restore(state) вместо ResetTransform(), если нужно сохранить предыдущие трансформации.

Задание по аналогии: Пульсирующий объект (Task17)

По аналогии с Task17_Transformations создайте анимацию с комбинированными трансформациями.

  1. Создайте поля float angle и float scale, а также Timer с интервалом 50 мс.
  2. В Timer_Tick: увеличивайте angle на 3, вычисляйте scale = 1 + 0.3f * Math.Sin(...), вызывайте Invalidate().
  3. В Paint: примените TranslateTransform к центру формы, затем RotateTransform(angle) и ScaleTransform(scale, scale).
  4. Нарисуйте прямоугольник со смещением от начала координат, чтобы он вращался вокруг центра.
  5. Сбросьте трансформации и нарисуйте подпись.

Пример результата выполнения задания:

Результат Task17

Скриншот программы (Task17)

Пример результата выполнения задания:

Результат Task17

Скриншот программы (Task17)

💡 Совет: Всегда применяйте трансформации в порядке: Translate → Rotate → Scale. Обратный порядок даст другой результат.

Проверка знаний

Ответьте на вопросы, чтобы проверить, насколько хорошо вы усвоили материал:

1. Какой метод используется для поворота системы координат?
  • ScaleTransform
  • TranslateTransform
  • RotateTransform
  • SkewTransform
2. Как сбросить все трансформации?
  • ResetTransform()
  • ClearTransform()
  • RemoveTransform()
  • DefaultTransform()
3. Какой метод используется для поворота графики?
  • RotateTransform
  • Rotate
  • TransformRotate
  • SetRotation