Лабораторная работа №3
«Аффинные преобразования на плоскости»
Цель работы: научиться выполнять простейшие аффинные преобразования на плоскости: перенос, сдвиг, поворот, масштабирование с зеркальным отражением. Написать программу, которая реализует последовательность этих преобразований для некоторого графического объекта.
Пусть имеется треугольник, однородные координаты вершин которого:
Выполним следующую последовательность преобразований:
1. Перенос на X = -3, Y = -5
2. Масштабирование относительно точки (1, 3): kx = 1, ky = -1
3. Поворот относительно точки (0, 8) на угол
4. Сдвиг относительно прямой y = 4 вдоль оси OX с параметром сдвига
kx = 1
1. Перенос на X = -3, Y = -5:
Общий вид матрицы переноса:
в данном случае ,
После переноса координаты вершин треугольника:
2. Масштабирование относительно точки (1, 3): kx = 1, ky = -1
Общий вид матрицы масштабирования:
Преобразование является сложным, поэтому надо выполнить последовательность преобразований:
а) перенос на X = -1, Y = -3
б) масштабирование с параметрами kx = 1, ky = -1
в) перенос на X = 1, Y = 3
Тогда матрица преобразования будет иметь вид:
,
После масштабирования координаты вершин треугольника:
3. Поворот относительно точки (0, 8) на угол
Общий вид матрицы поворота:
Преобразование является сложным, поэтому надо выполнить последовательность преобразований:
а) перенос на X = 0, Y = -8
б) поворот на угол
в) перенос на X = 0, Y = 8
Тогда матрица преобразования будет иметь вид:
После поворота координаты вершин треугольника:
4. Сдвиг относительно прямой y = 4 вдоль оси OX с параметром сдвига
kx = 1
Общий вид матрицы сдвига вдоль оси X:
Преобразование является сложным, поэтому надо выполнить последовательность преобразований:
а) перенос на X = 0, Y = -4
б) сдвиг вдоль оси OX с параметром сдвига kx = 1 в) перенос на X = 0, Y = 4
Тогда матрица преобразования будет иметь вид:
После сдвига координаты вершин треугольника:
Алгоритм работы программы:
Исходный текст программы на языке С#:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace FlatCurve
{
public partial class frmFlatCurve : Form
{
int aff_number = 0; //номер аффинного преобразования
int dkx = 20, dky = 20; // приращения в экранных точках при сдвиге начала координат
bool begin = false; //признак ввода начальных значений
float sizeX,sizeY; //масштаб по x и y
float stepX, stepY; // приращение по x и y в одном пикселе
float relX, relY; //координаты точки относительно начала координат
float[,] points = new float[3, 3]; // точки фигуры
PointF[] scr_points = new PointF[3]; // экранные точки
PointF XY = new PointF(); //текущая точка на экране
PointF center = new PointF(); // точка начала координат
public frmFlatCurve()
{
InitializeComponent();
center.X = pnlGrafik.Size.Width / 2-250;
center.Y = pnlGrafik.Size.Height / 2+150;
}
void mashtab(float kx,float ky) //масштабирование
{
float[,] matr = { { kx, 0, 0 }, { 0, ky, 0 }, { 0, 0, 1 } };
points = matr_mult(matr, points);
}
void perenos(float tx, float ty) //перенос
{
float[,] matr = { { 1, 0, tx }, { 0, 1, ty }, { 0, 0, 1 } };
points = matr_mult(matr, points);
}
void povorot(float ugol) //поворот на угол
{
float rad = (float) (ugol * Math.PI / 180);
float[,] matr = { {(float) Math.Cos(rad), (float)(-1*Math.Sin(rad)), 0 }, { (float) Math.Sin(rad), (float) Math.Cos(rad), 0 }, { 0, 0, 1 } };
points = matr_mult(matr, points);
}
void sdvig(float kx) //Сдвиг
{
float[,] matr = { { 1, kx, 0 }, { 0, 1, 0 }, { 0, 0, 1 } };
points = matr_mult(matr, points);
}
float[,] matr_mult(float[,] matr, float[,] points) //умножение матриц
{
int i, j, k;
float[,] rez = new float[3, 3];
for (i = 0; i < 3; i++)
for (j = 0; j < 3; j++)
{
rez[i, j] = 0;
for (k = 0; k < 3; k++)
rez[i, j] += matr[i, k] * points[k, j];
}
for (i = 0; i < 3; i++)
for (j = 0; j < 3; j++) points[i, j] = rez[i, j];
return rez;
}
//графическое построение
private void pnlGrafik_Paint(object sender, PaintEventArgs e)
{
Pen OXY = new Pen(Color.Blue, 2); //перо оси XOY
Pen setka = new Pen(Color.Gray, 1); //перо масштабной сетки
Pen figur = new Pen(Color.Red, 3); //перо для рисования фигуры
Graphics g = e.Graphics;
Font txt = new Font("Arial", 14, FontStyle.Bold);
int count = 0;
float cx, cy; //текущие точки плоскости XOY
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
g.FillRectangle(Brushes.White, 0, 0, pnlGrafik.Size.Width, pnlGrafik.Size.Height);
sizeX = int.Parse(mashtab_ox.Value.ToString());
sizeY = int.Parse(mashtab_oy.Value.ToString());
stepX = 1 / sizeX;
stepY = 1 / sizeY;
//рисование масштабной сетки по x
for (XY.X = center.X, count = 0; XY.X < pnlGrafik.Size.Width; XY.X += sizeX, count++)
{
g.DrawLine(setka, XY.X, 0, XY.X, pnlGrafik.Size.Height);
g.DrawString(count.ToString(), txt, Brushes.Black, XY.X - txt.Size, center.Y);
}
for (XY.X = center.X, count = 0; XY.X > 0; XY.X -= sizeX, count--)
{
g.DrawLine(setka, XY.X, 0, XY.X, pnlGrafik.Size.Height);
if (count != 0)
g.DrawString(count.ToString(), txt, Brushes.Black, XY.X - 1.5f * txt.Size, center.Y);
}
//по y
for (XY.Y = center.Y, count = 0; XY.Y > 0; XY.Y -= sizeY, count++)
{
g.DrawLine(setka, 0, XY.Y, pnlGrafik.Size.Width, XY.Y);
g.DrawString(count.ToString(), txt, Brushes.Black, center.X - txt.Size, XY.Y);
}
for (XY.Y = center.Y, count = 0; XY.Y < pnlGrafik.Size.Height; XY.Y += sizeY, count--)
{
g.DrawLine(setka, 0, XY.Y, pnlGrafik.Size.Width, XY.Y);
if (count != 0)
g.DrawString(count.ToString(), txt, Brushes.Black, center.X - 1.5f * txt.Size, XY.Y);
}
//рисование координатных осей
g.DrawLine(OXY, 0, center.Y, pnlGrafik.Size.Width, center.Y);
g.DrawLine(OXY, center.X, 0, center.X, pnlGrafik.Size.Height);
g.DrawString("0", txt, Brushes.Black, center.X - txt.Size, center.Y);
if (begin)
{
for (int i = 0; i < 3; i++) //преобразование точек плоскости в экранные
{
cx = points[0, i];
cy = points[1, i];
relX = cx / stepX;
relY = cy / stepY;
XY.X = relX + center.X;
XY.Y = center.Y - relY;
scr_points[i].X = XY.X;
scr_points[i].Y = XY.Y;
}
g.DrawLine(figur, scr_points[0], scr_points[1]); //рисование фигуры
g.DrawLine(figur, scr_points[1], scr_points[2]);
g.DrawLine(figur, scr_points[0], scr_points[2]);
}
}
//обновление после масштабирования изображения
private void btn_update_Click(object sender, EventArgs e)
{
this.pnlGrafik.Invalidate();
}
//сдвиг по оси X влево
private void btnXdown_Click(object sender, EventArgs e)
{
center.X += dkx;
this.pnlGrafik.Invalidate();
}
//сдвиг по оси X вправо
private void btnXup_Click(object sender, EventArgs e)
{
center.X -= dkx;
this.pnlGrafik.Invalidate();
}
//сдвиг по оси Y вниз
private void btnYdown_Click(object sender, EventArgs e)
{
center.Y -= dky;
this.pnlGrafik.Invalidate();
}
//сдвиг по оси Y вверх
private void btnYup_Click(object sender, EventArgs e)
{
center.Y += dky;
this.pnlGrafik.Invalidate();
}
//кнопка Выполнить
private void btnAFF_preob_Click(object sender, EventArgs e)
{
float kx, ky, rel_x,rel_y;
try
{
kx = float.Parse(txtparam_1.Text);
ky = float.Parse(txtparam_2.Text);
rel_x = float.Parse(txt_rel_point_x.Text);
rel_y = float.Parse(txt_rel_point_y.Text);
}
catch (Exception)
{
MessageBox.Show("Неправильно введены данные!");
return;
}
// в зависимости от выбранного преобразования
switch (aff_number)
{
case 1:
perenos(-rel_x, -rel_y);
perenos(kx, ky);
perenos(rel_x, rel_y);
break;
case 2:
perenos(-rel_x, -rel_y);
mashtab(kx, ky);
perenos(rel_x, rel_y);
break;
case 3:
perenos(-rel_x, -rel_y);
povorot(kx);
perenos(rel_x, rel_y);
break;
case 4:
perenos(0, -ky);
sdvig(kx);
perenos(0, ky);
label16.Visible = true;
label17.Visible = true;
label18.Visible = true;
txt_rel_point_x.Visible = true;
txt_rel_point_y.Visible = true;
break;
}
button_unlock();
this.pnlGrafik.Invalidate();
//вывод значений точек
txtX1.Text = points[0, 0].ToString();
txtX2.Text = points[0, 1].ToString();
txtX3.Text = points[0, 2].ToString();
txtY1.Text = points[1, 0].ToString();
txtY2.Text = points[1, 1].ToString();
txtY3.Text = points[1, 2].ToString();
}
void clear_txt()
{
txt_rel_point_x.Text = "0";
txt_rel_point_y.Text = "0";
txtparam_1.Text = "0";
txtparam_2.Text = "0";
}
private void btn_enter_Click(object sender, EventArgs e)
{
try
{
//X1,Y1
points[0, 0] = float.Parse(txtX1.Text);
points[1, 0] = float.Parse(txtY1.Text);
points[2, 0] = 1;
//X2,Y2
points[0, 1] = float.Parse(txtX2.Text);
points[1, 1] = float.Parse(txtY2.Text);
points[2, 1] = 1;
//X3,Y3
points[0, 2] = float.Parse(txtX3.Text);
points[1, 2] = float.Parse(txtY3.Text);
points[2, 2] = 1;
}
catch (Exception)
{
MessageBox.Show("Неправильно введены данные!");
return;
}
begin = true;
pnl_begin.Visible = false;
label4.Text = "Информация о точках:";
btn_enter.Visible = false;
clear_txt();
pnl_aff_preob.Visible = true;
pnlGrafik.Invalidate();
btnAFF_preob.BackColor = Color.FromArgb(150, 150, 150);
btnAFF_preob.Enabled = false;
}
void button_unlock()
{
btnAFF_preob.BackColor = Color.FromArgb(150, 150, 150);
btnAFF_preob.Enabled = false;
btn_translate.BackColor = Color.FromArgb(224, 224, 224);
btn_translate.Enabled = true;
btn_rotate.BackColor = Color.FromArgb(224, 224, 224);
btn_rotate.Enabled = true;
btn_mashtab.BackColor = Color.FromArgb(224, 224, 224);
btn_mashtab.Enabled = true;
btn_shift.BackColor = Color.FromArgb(224, 224, 224);
btn_shift.Enabled = true;
}
void button_lock()
{
btnAFF_preob.BackColor = Color.FromArgb(224, 224, 224);
btnAFF_preob.Enabled = true;
btn_translate.BackColor = Color.FromArgb(150, 150, 150);
btn_translate.Enabled = false;
btn_rotate.BackColor = Color.FromArgb(150, 150, 150);
btn_rotate.Enabled = false;
btn_mashtab.BackColor = Color.FromArgb(150, 150, 150);
btn_mashtab.Enabled = false;
btn_shift.BackColor = Color.FromArgb(150, 150, 150);
btn_shift.Enabled = false;
}
private void btn_translate_Click(object sender, EventArgs e)
{
aff_number = 1;
clear_txt();
label1.Text = "X = ";
label1.Visible = true;
txtparam_1.Visible = true;
label2.Text = "Y = ";
label2.Visible = true;
txtparam_2.Visible = true;
button_lock();
btn_translate.BackColor = Color.White;
}
private void btn_mashtab_Click(object sender, EventArgs e)
{
aff_number = 2;
clear_txt();
label1.Text = "kx = ";
label1.Visible = true;
txtparam_1.Visible = true;
label2.Text = "ky = ";
label2.Visible = true;
txtparam_2.Visible = true;
button_lock();
btn_mashtab.BackColor = Color.White;
}
private void btn_rotate_Click(object sender, EventArgs e)
{
aff_number = 3;
clear_txt();
label1.Text = "F = ";
label1.Visible = true;
txtparam_1.Visible = true;
label2.Visible = false;
txtparam_2.Visible = false;
button_lock();
btn_rotate.BackColor = Color.White;
}
private void btn_shift_Click(object sender, EventArgs e)
{
aff_number = 4;
clear_txt();
label1.Text = "kx = ";
label1.Visible = true;
label2.Text = "Y = ";
label2.Visible = true;
txtparam_2.Visible = true;
label16.Visible = false;
label17.Visible = false;
label18.Visible = false;
txt_rel_point_x.Visible = false;
txt_rel_point_y.Visible = false;
button_lock();
btn_shift.BackColor = Color.White;
}
private void pnlGrafik_Click(object sender, EventArgs e)
{
pnl_begin.Visible = true;
}
private void pnlGrafik_DoubleClick(object sender, EventArgs e)
{
pnl_begin.Visible = false;
}}
}
Работа программы: при запуске появляется окно, при этом необходимо задать начальные координаты объекта:
После ввода начальных координат появляется список преобразований:
Для выполнения преобразования необходимо нажать на соответствующую
Уважаемый посетитель!
Чтобы распечатать файл, скачайте его (в формате Word).
Ссылка на скачивание - внизу страницы.