Данный простой рецепт публикуется как один из исторических материалов, который является сейчас частью проекта R.I.P и поможет вам справится с отрисовкой многоугольников (и не только правильных), а также с задачей их закраски.
Код рисования и закраски прямоугольников использует идеи из гостевой статьи Тимура Гафарова про векторную графику и реализует несколько иной интерфейс: процедуры принимают на вход в качестве аргументов некоторую поверхность (в нашем случае, изображение на котором будем рисовать), цвет для рисования/окрашивания и массив координат вершин многоугольника (отдельно массив абсцисс и массив ординат). При этом код работает с любыми арифметическими типами, а также использует собственный код для отрисовки линии-отрезка (метод Брезенхэма, процедура реализации которого также принимает любые арифметические типы в качестве аргументов-координат).
Код реализации со всеми вспомогательными процедурами:
import dlib.image;
private
{
import std.meta : allSatisfy;
import std.traits : isIntegral, isFloatingPoint, Unqual;
}
// все типы арифметические ?
template allArithmetic(T...)
if (T.length >= 1)
{
template isNumberType(T)
{
enum bool isNumberType = isIntegral!(Unqual!T) || isFloatingPoint!(Unqual!T);
}
enum bool allArithmetic = allSatisfy!(isNumberType, T);
}
// draw line with Bresenham method
void drawBresenhamLine(T, U, V, W)(SuperImage surface, Color4f color, T x1, U y1, V x2, W y2)
if (allArithmetic!(T, U, V, W))
{
import std.algorithm;
import std.math;
float a = cast(float) x1;
float b = cast(float) y1;
float c = cast(float) x2;
float d = cast(float) y2;
float dx = (x2 - x1 >= 0 ? 1 : -1);
float dy = (y2 - y1 >= 0 ? 1 : -1);
float lengthX = abs(x2 - x1);
float lengthY= abs(y2 - y1);
float length = max(lengthX, lengthY);
if (length == 0)
{
surface[cast(int) x1, cast(int) y1] = color;
}
if (lengthY <= lengthX)
{
float x = x1;
float y = y1;
length++;
while (length--)
{
surface[cast(int) x, cast(int) y] = color;
x += dx;
y += (dy * lengthY) / lengthX;
}
}
else
{
float x = x1;
float y = y1;
length++;
while(length--)
{
surface[cast(int) x, cast(int) y] = color;
x += (dx * lengthX) / lengthY;
y += dy;
}
}
}
// Точка внутри многоугольника ?
bool insidePolygon(S, T, U, V)(S pointX, T pointY, U[] polyX, V[] polyY)
if (allArithmetic!(S, T, U, V))
{
import std.algorithm;
import std.conv;
import std.range;
assert(polyX.length == polyY.length);
bool inside = false;
auto px = cast(float) pointX;
auto py = cast(float) pointY;
auto poly_x = polyX.map!(a => to!float(a)).array;
auto poly_y = polyY.map!(a => to!float(a)).array;
size_t i = 0;
size_t j = poly_x.length - 1;
for (i = 0; i < poly_x.length; i++)
{
float ax = poly_x[i]; float ay = poly_y[i];
float bx = poly_x[j]; float by = poly_y[j];
if ((ay > py) != (by > py) && (px < (bx - ax) * (py - ay) / (by - ay) + ax))
inside = !inside;
j = i;
}
return inside;
}
// Нарисовать многоугольник
auto drawPolygon(S, T)(SuperImage surface, Color4f color, S[] polyX, T[] polyY)
if (allArithmetic!(S, T))
{
assert(polyX.length == polyY.length);
import std.algorithm;
import std.conv;
import std.range;
auto newSurface = surface.dup;
auto poly_x = polyX.map!(a => to!float(a)).array;
auto poly_y = polyY.map!(a => to!float(a)).array;
auto startX = poly_x[0], startY = poly_y[0];
foreach (i; 0..poly_x.length)
{
newSurface.drawBresenhamLine(color, startX, startY, poly_x[i], poly_y[i]);
startX = poly_x[i];
startY = poly_y[i];
}
newSurface.drawBresenhamLine(color, poly_x[0], poly_y[0], poly_x[$-1], poly_y[$-1]);
return newSurface;
}
// Закрасить многоугольник
auto fillPolygon(S, T)(SuperImage surface, Color4f color, S[] polyX, T[] polyY)
if (allArithmetic!(S, T))
{
assert(polyX.length == polyY.length);
auto newSurface = surface.dup;
foreach (float x; 0..surface.width)
{
foreach (float y; 0..surface.height)
{
if (insidePolygon(x, y, polyX, polyY))
{
newSurface[cast(int) x, cast(int) y] = color;
}
}
}
return newSurface;
}
Простейший эксперимент с закрашиванием:
void main()
{
SuperImage surface = image(256, 256);
Color4f color = Color4f(0.5f, 0.1f, 0.5f);
float[] xs = [1, 20, 40, 50, 20, 10];
float[] ys = [10, 20, 60, 70, 200, 150];
surface
.fillPolygon(color, xs, ys)
.fillPolygon(color, [190, 250, 190, 250], [200, 250, 250, 200])
.savePNG(`test.png`);
}
Результат:
