Компьютерная графика ЛР2. Афинные преобразования
Скачать 0.66 Mb.
|
МИНОБРНАУКИ РОССИИ Федеральное государственное бюджетное образовательное учреждение высшего профессионального образования «ЮГО-ЗАПАДНЫЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ» Лабораторная работа №3 По дисциплине «Компьютерная графика» На тему: «Афинные преобразования» Выполнил: Студент группы ПО-91з Нестеров С.Д. Проверил: Преподаватель Петрик Е.А. Курск, 2022 Задание Написать программу (на языке высокого уровня), позволяющую производить и отображать на экране масштабирование, поворот и перенос заданных 3 отрезков, а также трехмерной фигуры в виде куба. Координаты отрезков, длину стороны куба, коэффициенты для масштабирования и переноса должны задаваться пользователем. Блок-схемы алгоритмов Блок-схема переноса отрезка изображена Рисунок 1 – Блок-схема переноса отрезка. Рисунок 1 – Блок-схема переноса отрезка Блок-схема масштабирования отрезка изображена Рисунок 2 – Блок-схема масштабирования отрезка. Рисунок 2 – Блок-схема масштабирования отрезка Блок-схема поворота отрезка изображена Рисунок 3 – Блок-схема поворота отрезка. Рисунок 3 – Блок-схема поворота отрезка Блок-схема трёхмерного переноса изображена Рисунок 4 – Блок-схема трёхмерного переноса. Рисунок 4 – Блок-схема трёхмерного переноса Блок-схема трёхмерного масштабирования изображена Рисунок 5 – Блок-схема трёхмерного масштабирования. Рисунок 5 – Блок-схема трёхмерного масштабирования Блок-схема трёхмерного поворота изображена на рисунках Рисунок 6 – Блок-схема трёхмерного поворота и Рисунок 7 – Блок-схема трёхмерного поворота. Рисунок 6 – Блок-схема трёхмерного поворота Рисунок 7 – Блок-схема трёхмерного поворота Листинг программы public struct Pixel { private int _x; private int _y; public int X { get => _x; set => _x = value; } public int Y { get => _y; set => _y = value; } public Pixel(int x, int y) { _x = x; _y = y; } } public struct Point2D { private float _x; private float _y; public float X { get => _x; set => _x = value; } public float Y { get => _y; set => _y = value; } public Point2D(float x, float y) { _x = x; _y = y; } public void Move(float dx, float dy) { _x += dx; _y += dy; } public void Scale(float sx, float sy) { if (sx != 0) _x *= sx; if (sx != 0) _y *= sy; } public void Rotate(float angle) { if (angle == 0) return; double rAngle = angle * Math.PI / 180; float tempX = _x * (float)Math.Cos(rAngle) - _y * (float)Math.Sin(rAngle); _y = _x * (float)Math.Sin(rAngle) + _y * (float)Math.Cos(rAngle); _x = tempX; } } public struct Point3D { private float _x; private float _y; private float _z; public float X { get => _x; set => _x = value; } public float Y { get => _y; set => _y = value; } public float Z { get => _z; set => _z = value; } public Point3D(float x, float y, float z) { _x = x; _y = y; _z = z; } public void Move(float dx, float dy, float dz) { _x += dx; _y += dy; _z += dz; } public void Scale(float sx, float sy, float sz) { if (sx != 0) _x *= sx; if (sy != 0) _y *= sy; if (sz != 0) _z *= sz; } public void Rotate(float ax, float ay, float az) { double rAngle = 0; float[][] r = null; float[][] s = new float[][] { new float[] { _x, _y, _z, 1 }, }; if (ax != 0) { rAngle = ax * Math.PI / 180; r = new float[][] { new float[]{1,0,0,0}, new float[]{0, (float)Math.Cos(rAngle), (float)Math.Sin(rAngle),0}, new float[]{0, -(float)Math.Sin(rAngle), (float)Math.Cos(rAngle),0}, new float[]{0,0,0,1} }; s = MatrixMultiply(s, r); } if (ay != 0) { rAngle = ay * Math.PI / 180; r = new float[][] { new float[]{ (float)Math.Cos(rAngle), 0, -(float)Math.Sin(rAngle), 0}, new float[]{0,1,0,0}, new float[]{ (float)Math.Sin(rAngle),0,(float)Math.Cos(rAngle),0}, new float[]{0,0,0,1} }; s = MatrixMultiply(s, r); } if (az != 0) { rAngle = az * Math.PI / 180; r = new float[][] { new float[]{ (float)Math.Cos(rAngle),(float)Math.Sin(rAngle),0, 0}, new float[]{ -(float)Math.Sin(rAngle), (float)Math.Cos(rAngle), 0,0}, new float[]{ 0,0,1,0}, new float[]{0,0,0,1} }; s = MatrixMultiply(s, r); } _x = s[0][0]; _y = s[0][1]; _z = s[0][2]; } } public interface IGraficObject : INotifyPropertyChanged { Guid Id { get; } Color Color { get; set; } Pixel[] LastPixels { get; } Pixel[] GetPixels(); } public interface IFigure : IGraficObject { LineAlgType AlgType { get; set; } int[] Edges { get; } } public interface IFigure2D : IFigure { float X { get; set; } float Y { get; set; } Point2D[] LocalVertexes { get; } Point2D[] GlobalVertexes { get; set; } void Move(float dx, float dy); void Scale(float sx, float sy); void Rotate(float angle); } public interface IFigure3D : IFigure { float X { get; set; } float Y { get; set; } float Z { get; set; } float EdgeLength { get; set; } ProjectionType ProjectionType { get; set; } Point3D[] LocalVertexes { get; } Point3D[] GlobalVertexes { get; } void Move(float dx, float dy, float dz); void Scale(float sx, float sy, float sz); void Rotate(float ax, float ay, float az); } public abstract class BaseGraficObject : NotifyPropertyChange, IGraficObject { protected Color _color; protected Pixel[] _lastPixels; public Guid Id { get; } = Guid.NewGuid(); public Color Color { get => _color; set { _color = value; NotifyPropertyChanged(nameof(Color)); } } public Pixel[] LastPixels { get => _lastPixels; protected set => _lastPixels = value; } public abstract Pixel[] GetPixels(); protected BaseGraficObject(Color color) { _color = color; } } public abstract class BaseFigure : BaseGraficObject , IFigure { protected LineAlgType _algType; protected int[] _edges; public LineAlgType AlgType { get => _algType; set => Set(ref _algType, value); } public int[] Edges { get => _edges; } protected BaseFigure(LineAlgType algType, Color color) : base(color) { _algType = algType; } } public abstract class BaseFigure2D : BaseFigure, IFigure2D { protected float _x; protected float _y; protected Point2D[] _localVertexes; public float X { get => _x; set => Set(ref _x, value); } public float Y { get => _y; set => Set(ref _y, value); } public Point2D[] LocalVertexes { get => _localVertexes; protected set { Set(ref _localVertexes, value); NotifyPropertyChanged(nameof(GlobalVertexes)); } } public Point2D[] GlobalVertexes { get => LocalVertexes.Select(p => ToGlobal(p)).ToArray(); set => LocalVertexes = value.Select(p => ToLocal(p)).ToArray(); } protected BaseFigure2D(float x, float y, LineAlgType algType, Color color) : base(algType, color) { _x = x; _y = y; } protected BaseFigure2D(float x, float y, Color color) : this(x, y, LineAlgType.BrezenhamInteger, color) { } protected BaseFigure2D(float x, float y, LineAlgType algType) : this(x, y, algType, Color.Black) { } protected BaseFigure2D(float x, float y) : this(x, y, Color.Black) { } public Point2D ToGlobal(Point2D point) { return new Point2D(point.X + _x, point.Y + _y); } public Point2D ToLocal(Point2D point) { return new Point2D(point.X - _x, point.Y - _y); } public override Pixel[] GetPixels() { return LastPixels = RasterizeService.GetPixels(GlobalVertexes, Edges, Enums.LineAlgType.DDA); } public void Move(float dx, float dy) { X += dx; Y += dy; NotifyPropertyChanged(nameof(GlobalVertexes)); } public void Rotate(float angle) { for (int i = 0; i < _localVertexes.Length; i++) { _localVertexes[i].Rotate(angle); } NotifyPropertyChanged(nameof(LocalVertexes)); NotifyPropertyChanged(nameof(GlobalVertexes)); } public void Scale(float sx, float sy) { for (int i = 0; i < _localVertexes.Length; i++) { _localVertexes[i].Scale(sx, sy); } NotifyPropertyChanged(nameof(LocalVertexes)); NotifyPropertyChanged(nameof(GlobalVertexes)); } } public abstract class BaseFigure3D : BaseFigure, IFigure3D { protected float _x; protected float _y; protected float _z; protected float _edgeLength; protected ProjectionType _projectionType; protected Point3D[] _localVertexes; public float X { get => _x; set => Set(ref _x, value); } public float Y { get => _y; set => Set(ref _y, value); } public float Z { get => _z; set => Set(ref _z, value); } public float EdgeLength { get => _edgeLength; set { float scaleCoeff = value / _edgeLength; if (scaleCoeff != 1) Scale(scaleCoeff, scaleCoeff, scaleCoeff); } } public Point3D[] LocalVertexes { get => _localVertexes; } public Point3D[] GlobalVertexes { get => _localVertexes.Select(p => ToGlobal(p)).ToArray(); } public ProjectionType ProjectionType { get => _projectionType; set => Set(ref _projectionType, value); } protected BaseFigure3D(float x, float y, float z, float edgeLength, LineAlgType algType, ProjectionType projectionType, Color color) : base(algType, color) { _x = x; _y = y; _z = z; _edgeLength = edgeLength; _projectionType = projectionType; } protected BaseFigure3D(float x, float y, float z, float edgeLength) : this(x, y, z, edgeLength, LineAlgType.BrezenhamInteger, ProjectionType.Ortogonal, Color.Black) { } public Point3D ToGlobal(Point3D point) { return new Point3D(point.X + _x, point.Y + _y, point.Z + _z); } public Point3D ToLocal(Point3D point) { return new Point3D(point.X - _x, point.Y - _y, point.Z - _z); } public override Pixel[] GetPixels() { return LastPixels = RasterizeService.GetPixels(GlobalVertexes, Edges, Enums.LineAlgType.DDA); } public void Move(float dx, float dy, float dz) { X += dx; Y += dy; Z += dz; NotifyPropertyChanged(nameof(GlobalVertexes)); } public void Rotate(float ax, float ay, float az) { for (int i = 0; i < _localVertexes.Length; i++) { _localVertexes[i].Rotate(ax, ay, az); } NotifyPropertyChanged(nameof(LocalVertexes)); NotifyPropertyChanged(nameof(GlobalVertexes)); } public void Scale(float sx, float sy, float sz) { for (int i = 0; i < _localVertexes.Length; i++) { _localVertexes[i].Scale(sx, sy, sz); } _edgeLength = (float)Math.Sqrt( Math.Pow(LocalVertexes[_edges[1]].X - LocalVertexes[_edges[0]].X, 2) + Math.Pow(LocalVertexes[_edges[1]].Y - LocalVertexes[_edges[0]].Y, 2) + Math.Pow(LocalVertexes[_edges[1]].Z - LocalVertexes[_edges[0]].Z, 2) ); NotifyPropertyChanged(nameof(EdgeLength)); NotifyPropertyChanged(nameof(LocalVertexes)); NotifyPropertyChanged(nameof(GlobalVertexes)); } } public class Line2D : BaseFigure2D { public Line2D(Point2D p0, Point2D p1, LineAlgType algType, Color color) : base ( (int)(p0.X + p1.X) / 2, (int)(p0.Y + p1.Y) / 2, algType, color ) { _localVertexes = new Point2D[] { ToLocal(p0), ToLocal(p1), }; _edges = new int[] { 0,1 }; } public Line2D(int x0, int y0, int x1, int y1, LineAlgType algType, Color color) : this(new Point2D(x0, y0), new Point2D(x1, y1), algType, color) { } public Line2D(int x0, int y0, int x1, int y1, LineAlgType algType) : this(x0, y0, x1, y1, algType, Color.Black) { } public Line2D(int x0, int y0, int x1, int y1) : this(x0, y0, x1, y1, LineAlgType.DDA) { } } public class Cube : BaseFigure3D { public Cube(float x, float y, float z, float edgeLength, LineAlgType algType, ProjectionType projectionType, Color color) : base(x, y, z, edgeLength, algType, projectionType, color) { _localVertexes = new Point3D[] { new Point3D(-edgeLength/2,-edgeLength/2,-edgeLength/2),//0 new Point3D(-edgeLength/2,-edgeLength/2,+edgeLength/2),//1 new Point3D(+edgeLength/2,-edgeLength/2,-edgeLength/2),//2 new Point3D(+edgeLength/2,-edgeLength/2,+edgeLength/2),//3 new Point3D(-edgeLength/2,+edgeLength/2,-edgeLength/2),//4 new Point3D(-edgeLength/2,+edgeLength/2,+edgeLength/2),//5 new Point3D(+edgeLength/2,+edgeLength/2,-edgeLength/2),//6 new Point3D(+edgeLength/2,+edgeLength/2,+edgeLength/2),//7 }; _edges = new int[] { 0,1, 0,2, 0,4, 1,3, 1,5, 2,3, 2,6, 3,7, 4,5, 4,6, 5,7, 6,7, }; } } public static class MatrixOperationService { public static float[][] MatrixMultiply(float[][] matrixA, float[][] matrixB) { int aRows = matrixA.Length; int aCols = matrixA[0].Length; int bRows = matrixB.Length; int bCols = matrixB[0].Length; if (aCols != bRows) throw new Exception("Non-conformable matrices in MatrixProduct"); float[][] result = MatrixCreate(aRows, bCols); Parallel.For(0, aRows, i => { for (int j = 0; j < bCols; ++j) for (int k = 0; k < aCols; ++k) result[i][j] += matrixA[i][k] * matrixB[k][j]; } ); return result; } public static float[][] MatrixCreate(int rows, int cols) { float[][] result = new float[rows][]; for (int i = 0; i < rows; ++i) result[i] = new float[cols]; return result; } } public static class RasterizeService { public static Pixel[] GetPixels(Point2D[] points, int[] edges, LineAlgType algType) { List pixels = new List (); for (int i = 0; i < edges.Length; i += 2) { Pixel[] tempPixels = GetPixels(points[edges[i]], points[edges[i + 1]], algType); if (tempPixels?.Length > 0) pixels.AddRange(tempPixels); } return pixels.ToArray(); } public static Pixel[] GetPixels(Point3D[] points, int[] edges, LineAlgType algType) { List pixels = new List (); Point2D[] points2D = points.Select(p => new Point2D(p.X, p.Y)).ToArray(); for (int i = 0; i < edges.Length; i += 2) { Pixel[] tempPixels = GetPixels(points2D[edges[i]], points2D[edges[i + 1]], algType); if (tempPixels?.Length > 0) pixels.AddRange(tempPixels); } return pixels.ToArray(); } public static Pixel[] GetPixels(Point2D p0, Point2D p1, LineAlgType algType) { switch (algType) { case LineAlgType.BrezenhamReal: { return CalcBrezenhamReal(p0, p1); } case LineAlgType.BrezenhamInteger: { return CalcBrezenhamInteger(p0, p1); } case LineAlgType.DDA: default: { return CalcDDA(p0, p1); } } } private static Pixel[] CalcDDA(Point2D p0, Point2D p1) { List result = new List (); if (p0.X == p1.X && p0.Y == p1.Y) { return null; } double L = (Math.Abs(p1.X - p0.X) >= Math.Abs(p1.Y - p0.Y)) ? Math.Abs(p1.X - p0.X) : Math.Abs(p1.Y - p0.Y); double dx = (p1.X - p0.X) / L; double dy = (p1.Y - p0.Y) / L; double x = p0.X + 0.5 * Math.Sign(dx); double y = p0.Y + 0.5 * Math.Sign(dy); for (int i = 1; i <= L + 1; i++) { result.Add(new Pixel((int)Math.Truncate(x), (int)Math.Truncate(y))); x += dx; y += dy; } return result.ToArray(); } private static Pixel[] CalcBrezenhamReal(Point2D p0, Point2D p1) { List result = new List (); if (p0.X == p1.X && p0.Y == p1.Y) { return null; } var steep = Math.Abs(p1.Y - p0.Y) > Math.Abs(p1.X - p0.X); bool rever = false; if (steep) { p0 = new Point2D(p0.Y, p0.X); p1 = new Point2D(p1.Y, p1.X); rever = true; } if (p0.X > p1.X) { Swap(ref p0, ref p1); } float dx = p1.X - p0.X; float dy = p1.Y - p0.Y; float sx = Math.Sign(dx); float sy = Math.Sign(dy); dx = Math.Abs(dx); dy = Math.Abs(dy); bool flag = false; if (dy > dx) { Swap(ref dx, ref dy); flag = true; } float f = dy / dx - 0.5f; float x = p0.X; float y = p0.Y; while (x <= p1.X) { result.Add(new Pixel ( (rever) ? (int)Math.Truncate(y) : (int)Math.Truncate(x), (rever) ? (int)Math.Truncate(x) : (int)Math.Truncate(y) )); if (f >= 0) { if (flag) { x += sx; } else { y += sy; } f--; } else { if (flag) { y += sy; } else { x += sx; } f += dy / dx; } } return result.ToArray(); } private static Pixel[] CalcBrezenhamInteger(Point2D p0, Point2D p1) { List result = new List (); if (p0.X == p1.X && p0.Y == p1.Y) { return null; } var steep = Math.Abs(p1.Y - p0.Y) > Math.Abs(p1.X - p0.X); bool rever = false; if (steep) { p0 = new Point2D(p0.Y, p0.X); p1 = new Point2D(p1.Y, p1.X); rever = true; } if (p0.X > p1.X) { Swap(ref p0, ref p1); } float dx = p1.X - p0.X; float dy = p1.Y - p0.Y; int sx = Math.Sign(dx); int sy = Math.Sign(dy); dx = Math.Abs(dx); dy = Math.Abs(dy); bool flag = false; if (dy > dx) { Swap(ref dx, ref dy); flag = true; } float fu = 2 * dy - dx; int x = (int)p0.X; int y = (int)p0.Y; while (x <= p1.X) { result.Add(new Pixel ( (rever) ? y : x, (rever) ? x : y )); if (fu >= 0) { if (flag) { x += sx; } else { y += sy; } fu -= 2 * dx; } else { if (flag) { y += sy; } else { x += sx; } fu += 2 * dy; } } return result.ToArray(); } public static Pixel[] CirclePixels(Point2D center, float radius) { List result = new List (); float x = 0; float y = radius; float delta = 1 - 2 * radius; float error = 0; while (y >= x) { result.Add(new Pixel((int)(center.X + x), (int)(center.Y + y))); result.Add(new Pixel((int)(center.X + x), (int)(center.Y - y))); result.Add(new Pixel((int)(center.X - x), (int)(center.Y + y))); result.Add(new Pixel((int)(center.X - x), (int)(center.Y - y))); result.Add(new Pixel((int)(center.X + y), (int)(center.Y + x))); result.Add(new Pixel((int)(center.X + y), (int)(center.Y - x))); result.Add(new Pixel((int)(center.X - y), (int)(center.Y + x))); result.Add(new Pixel((int)(center.X - y), (int)(center.Y - x))); error = 2 * (delta + y) - 1; if ((delta < 0) && (error <= 0)) { delta += 2 * ++x + 1; continue; } if ((delta > 0) && (error > 0)) { delta -= 2 * --y + 1; continue; } delta += 2 * (++x - --y); } return result.ToArray(); } } public class CustomCanvas : NotifyPropertyChange { protected Bitmap _dataCanvas; protected ObservableCollection private readonly int _width; private readonly int _height; protected readonly int _length; protected readonly Color _color; public Bitmap DataCanvas { get { return _dataCanvas; } private set { Set(ref _dataCanvas, value); } } public ObservableCollection { get => _graficObjects; set { Set(ref _graficObjects, value); } } public int Width => _width; public int Height => _height; public CustomCanvas(int width, int height, int length, Color color) { DataCanvas = new Bitmap(width, height); _width = width; _height = height; _length = length; _color = color; ClearCanvas(DataCanvas); GraficObjects.CollectionChanged += GraficObjects_CollectionChanged; } protected void ClearCanvas(Bitmap bitmap) { using (var g = Graphics.FromImage(bitmap)) { g.Clear(_color); } } public void DrawPixel(Bitmap bitmap, int x, int y, Color color) { if (x >= DataCanvas.Width || y >= DataCanvas.Height || x <= 0 || y <= 0) { return; } bitmap.SetPixel(x, y, color); } public void DrawGraficObject(IGraficObject graficObject) { Bitmap result = new Bitmap(Width, Height); ClearCanvas(result); if (!_graficObjects.Any(p => p.Id == graficObject.Id)) { _graficObjects.Add(graficObject); graficObject.PropertyChanged += GraficObject_PropertyChanged; } RedrawCanvas(result); DataCanvas = result; } private void RedrawCanvas(Bitmap bitmap) { foreach (IGraficObject graficObject in _graficObjects) { Pixel[] pixels = null; switch (graficObject) { case IFigure3D f3D: Point3D[] points = ProjectionService.Project(f3D, _camera); pixels = RasterizeService.GetPixels(points, f3D.Edges, f3D.AlgType); break; case IFigure2D f2D: case IGraficObject go: default: pixels = graficObject.GetPixels(); break; } DrawPixels(bitmap, pixels, graficObject.Color); } } public void DrawPixels(Bitmap bitmap, Pixel[] pixels, Color color) { foreach (Pixel p in pixels) { DrawPixel(bitmap, p.X, p.Y, color); } } private void GraficObject_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { DrawGraficObject(sender as IGraficObject); } public void Move(IFigure2D figure, float dx, float dy) { figure.Move(dx, dy); DrawGraficObject(figure); } public void Scale(IFigure2D figure, float sx, float sy) { figure.Scale(sx, sy); DrawGraficObject(figure); } public void Rotate(IFigure2D figure, float angle) { figure.Rotate(angle); DrawGraficObject(figure); } public void Move(IFigure3D figure, float dx, float dy, float dz) { figure.Move(dx, dy, dz); DrawGraficObject(figure); } public void Scale(IFigure3D figure, float sx, float sy, float sz) { figure.Scale(sx, sy, sz); DrawGraficObject(figure); } public void Rotate(IFigure3D figure, float ax, float ay, float az) { figure.Rotate(ax, ay, az); DrawGraficObject(figure); } private void GraficObjects_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { NotifyPropertyChanged(nameof(GraficObjects)); } } Пример работы программы Главное окно приложения изображено Рисунок 8 – Главное окно программы. Рисунок 8 – Главное окно программы Окно добавления фигур изображено Рисунок 9 – Окно добавления фигуры. Рисунок 9 – Окно добавления фигуры Добавление линии изображено Рисунок 10 – Добавление линии. Рисунок 10 – Добавление линии Добавление куба изображено Рисунок 11 – Добавление . Рисунок 11 – Добавление куба Результат добавления фигур изображён Рисунок 12 – . Рисунок 12 – Результат добавления фигур Результат масштабирования линии изображён Рисунок 13 – . Рисунок 13 – Результат масштабирования линии Смещение линии изображено Рисунок 14 – Смещение линии изображено. Рисунок 14 – Смещение линии изображено Поворот линии изображён Рисунок 15 – Поворот линии. Рисунок 15 – Поворот линии Результат масштабирования куба изображён Рисунок 16 – Результат масштабирования куба. Рисунок 16 – Результат масштабирования куба Смещение куба изображено Рисунок 17 – Смещение куба. Рисунок 17 – Смещение куба Результат поворота куба изображён Рисунок 18 – Результат поворота куба. Рисунок 18 – Результат поворота куба Ответы на контрольные вопросы Что такое афинные преобразования? Аффинными преобразованиями называют три базовые операции: перенос (перемещение) изображения; масштабирование (увеличение или уменьшение размеров) изображения; поворот изображения (употребляют также термины вращение, изменение ориентации). С помощью каких формул задаются афинные преобразования? Двумерные аффинные преобразования Параллельный перенос Масштабирование Поворот Приведите пример расчетов двумерных афинных преобразований для отрезка (координаты выбрать самостоятельно) P1 = [10 ; 20] P2 = [30 ; 50] Перенос по x на 10, по y на 12 x′1 = 10 +10 = 20 y′1 = 20 +12 = 32 x′2 = 30 +10 = 40 y′2 = 50 +12 = 62 P′1 = [20 ; 32] P′2 = [40 ; 62] Масштабирование в 1.5 по обеим координатам x′1 = 10 * 1.5 = 15 y′1 = 20 * 1.5 = 30 x′2 = 30 * 1.5 = 45 y′2 = 50 * 1.5 = 75 P′1 = [15 ; 30] P′2 = [45 ; 75] Поворот на 75⁰ x′1 = 10 * 0.259 – 30 * 0.966 = -16.73 y′1 = 10 * 0.966 + 30 * 0.259 = 14.836 x′2 = 30 * 0.259 – 50 * 0.966 = -40.532 y′2 = 30 * 0.966 + 50 * 0.259 = 41.919 P′1 = [-16.73 ; 14.836] P′2 = [-40.532 ; 41.919] |