Компьютерная графика ЛР1. Алгоритмы растеризации отрезков
Скачать 417.87 Kb.
|
МИНОБРНАУКИ РОССИИ Федеральное государственное бюджетное образовательное учреждение высшего профессионального образования «ЮГО-ЗАПАДНЫЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ» Лабораторная работа №1 По дисциплине «Компьютерная графика» На тему: «Алгоритмы растеризации отрезков» Выполнил: Студент группы ПО-91з Нестеров С.Д. Проверил: Преподаватель Петрик Е.А. Курск, 2022 Задание Написать программу (на языке высокого уровня), реализующую алгоритмы растеризации отрезков с их последующей отрисовкой. Координаты отрезка должны задаваться пользователем. Реализовать: алгоритм ЦДА; алгоритм Брезенхема; целочисленный алгоритм Брезенхема. Блок-схемы алгоритмов Блок-схема алгоритма ЦДА изображена Рисунок 1 – Блок-схема алгоритма ЦДА. Рисунок 1 – Блок-схема алгоритма ЦДА Блок-схема вещественного алгоритма Брезенхема изображена на рисунках Рисунок 2 – Блок-схема вещественного алгоритма Брезенхема и Рисунок 3 – Блок-схема вещественного алгоритма Брезенхема. Рисунок 2 – Блок-схема вещественного алгоритма Брезенхема Рисунок 3 – Блок-схема вещественного алгоритма Брезенхема Блок-схема целочисленного алгоритма Брезенхема изображена на рисунках Рисунок 4 – Блок-схема целочисленного алгоритма Брезенхема и Рисунок 5 – Блок-схема целочисленного алгоритма Брезенхема. Рисунок 4 – Блок-схема целочисленного алгоритма Брезенхема Рисунок 5 – Блок-схема целочисленного алгоритма Брезенхема Листинг программы 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 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 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 IFigure : IGraficObject { LineAlgType AlgType { get; set; } int[] Edges { get; } } public interface IGraficObject : INotifyPropertyChanged { Guid Id { get; } Color Color { get; set; } Pixel[] LastPixels { get; } Pixel[] GetPixels(); } 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 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 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 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); } private void GraficObjects_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { NotifyPropertyChanged(nameof(GraficObjects)); } } public static class Swapper { public static void Swap { (variable1, variable0) = (variable0, variable1); } } 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(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(); } } Пример работы программы Главное окно приложения изображено Рисунок 6 – Главное окно программы. Рисунок 6 – Главное окно программы Окно добавления фигур изображено Рисунок 7 – Окно добавления фигуры. Рисунок 7 – Окно добавления фигуры Добавление линии, построенной по алгоритму ЦДА изображено Рисунок 8 – Добавление линии, построенной по алгоритму ЦДА. Рисунок 8 – Добавление линии, построенной по алгоритму ЦДА Добавление линии, построенной по вещественному алгоритму Брезенхема изображено Рисунок 9 – Добавление линии, построенной по вещественному алгоритму Брезенхема. Рисунок 9 – Добавление линии, построенной по вещественному алгоритму Брезенхема Добавление линии, построенной по целочисленному алгоритму Брезенхема изображено Рисунок 10 – Добавление линии, построенной по целочисленному алгоритму Брезенхема. Рисунок 10 – Добавление линии, построенной по целочисленному алгоритму Брезенхема Результат добавления линий изображён Рисунок 11 – Результат добавления линий. Рисунок 11 – Результат добавления линий Ответы на контрольные вопросы Что такое пиксель? Пиксель – это минимальная единица, элемент, из чего состоит изображение – точка определенного цвета, которая выводится на экране в заданном месте. Что такое растровое изображение? Изображение, представляющее собой сетку пикселей - цветных точек на мониторе, бумаге и других отображающих устройствах. Для чего нужны алгоритмы растеризации отрезков? Графические примитивы имеют непрерывную (аналоговую) природу, а изображение строится, как правило, на экране растрового дисплея, то есть получаемое изображение носит дискретный характер. Какие ещё алгоритмы растеризации отрезков существуют? Алгоритм DDA-линии – простой алгоритм, использующий вещественную арифметику. Алгоритм Брезенхэма – оптимизированный алгоритм, использующий целочисленную арифметику и только операции сложения и вычитания. Алгоритм Ву – модифицированный алгоритм Брезенхэма, обеспечивающий сглаживание. |