Файл: Алгоритмы растеризации отрезков.docx

ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 12.04.2024

Просмотров: 9

Скачиваний: 0

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

МИНОБРНАУКИ РОССИИ

Федеральное государственное бюджетное образовательное учреждение

высшего профессионального образования

«ЮГО-ЗАПАДНЫЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ»

Лабораторная работа №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 _graficObjects = new 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 GraficObjects

{

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(ref T variable0, ref T variable1)

{

(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 – Результат добавления линий

Ответы на контрольные вопросы


  1. Что такое пиксель?

Пиксель – это минимальная единица, элемент, из чего состоит изображение – точка определенного цвета, которая выводится на экране в заданном месте.


  1. Что такое растровое изображение?

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


  1. Для чего нужны алгоритмы растеризации отрезков?

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


  1. Какие ещё алгоритмы растеризации отрезков существуют?

  • Алгоритм DDA-линии – простой алгоритм, использующий вещественную арифметику.

  • Алгоритм Брезенхэма – оптимизированный алгоритм, использующий целочисленную арифметику и только операции сложения и вычитания.

  • Алгоритм Ву – модифицированный алгоритм Брезенхэма, обеспечивающий сглаживание.