Данный простой рецепт публикуется как один из исторических материалов, который является сейчас частью проекта 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`); }
Результат: