ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 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
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-линии – простой алгоритм, использующий вещественную арифметику. -
Алгоритм Брезенхэма – оптимизированный алгоритм, использующий целочисленную арифметику и только операции сложения и вычитания. -
Алгоритм Ву – модифицированный алгоритм Брезенхэма, обеспечивающий сглаживание.