Skip to content

Commit

Permalink
Fix fillPolygon to better handle concave polygons
Browse files Browse the repository at this point in the history
  • Loading branch information
brendan-duncan committed Dec 20, 2024
1 parent 25fc92e commit 6a4234d
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 55 deletions.
71 changes: 17 additions & 54 deletions lib/src/draw/fill_polygon.dart
Original file line number Diff line number Diff line change
Expand Up @@ -64,65 +64,28 @@ Image fillPolygon(Image src,
xMax = min(xMax, src.width - 1);
yMax = min(yMax, src.height - 1);

final inter = List<num>.filled(40, 0);
final vi =
List<int>.generate(numVertices + 1, (i) => i < numVertices ? i : 0);

for (var yi = yMin, y = yMin + 0.5; yi <= yMax; ++yi, ++y) {
var c = 0;
for (var i = 0; i < numVertices; ++i) {
final v1 = vertices[vi[i]];
final v2 = vertices[vi[i + 1]];

var x1 = v1.x;
var y1 = v1.y;
var x2 = v2.x;
var y2 = v2.y;
if (y2 < y1) {
var temp = x1;
x1 = x2;
x2 = temp;
temp = y1;
y1 = y2;
y2 = temp;
}

if (y <= y2 && y >= y1) {
num x = 0;
if ((y1 - y2) == 0) {
x = x1;
} else {
x = ((x2 - x1) * (y - y1)) / (y2 - y1);
x = x + x1;
}
if (x <= xMax && x >= xMin) {
inter[c++] = x;
// Function to fill a complex polygon using the ray casting algorithm
for (var yi = yMin, y = yMin + 0.5; yi <= yMax; ++yi, y += 1.0) {
for (var xi = xMin, x = xMin + 0.5; xi <= xMax; ++xi, x += 1.0) {
var intersections = 0;
for (var vi = 0; vi < numVertices; ++vi) {
final v1 = vertices[vi];
final v2 = vertices[(vi + 1) % numVertices];
// Ray casting: cast a ray to the right (x increasing)
if (v1.y <= y && v2.y > y || v2.y <= y && v1.y > y) {
// Ray intersects the edge (vertical check)
final vt = (y - v1.y) / (v2.y - v1.y);
if (x < v1.x + vt * (v2.x - v1.x)) { // Horizontal check
intersections++;
}
}
}
}

for (var i = 0; i < c; i += 2) {
var x1f = inter[i];
var x2f = inter[i + 1];
if (x1f > x2f) {
final t = x1f;
x1f = x2f;
x2f = t;
}
final x1 = x1f.floor();
final x2 = x2f.ceil();
for (var x = x1; x <= x2; ++x) {
drawPixel(src, x, yi, color, mask: mask, maskChannel: maskChannel);
// Even number of intersections means inside
if (intersections & 0x1 == 1) {
drawPixel(src, xi, yi, color, mask: mask, maskChannel: maskChannel);
}
}
}

/*for (var i = 0; i < numVertices; ++i) {
final v1 = vertices[vi[i]];
final v2 = vertices[vi[i + 1]];
drawLine(src, x1: v1.xi, y1: v1.yi, x2: v2.xi, y2: v2.yi,
color: color, mask: mask, maskChannel: maskChannel, thickness: 1);
}*/

return src;
}
21 changes: 20 additions & 1 deletion test/draw/fill_polygon_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,30 @@ void main() {
];

fillPolygon(i0, vertices: vertices, color: ColorRgb8(176, 0, 0));
//drawPolygon(i0, vertices: vertices, color: ColorRgb8(0, 255, 0));
drawPolygon(i0, vertices: vertices, color: ColorRgb8(0, 255, 0));

File('$testOutputPath/draw/fillPolygon.png')
..createSync(recursive: true)
..writeAsBytesSync(encodePng(i0));
});

test('fillPolygon concave', () async {
final i0 = Image(width: 256, height: 256);

final vertices = <Point>[
Point(50, 50),
Point(50, 150),
Point(150, 150),
Point(150, 50),
Point(100, 100),
];

fillPolygon(i0, vertices: vertices, color: ColorRgb8(176, 0, 0));
drawPolygon(i0, vertices: vertices, color: ColorRgb8(0, 255, 0));

File('$testOutputPath/draw/fillPolygon2.png')
..createSync(recursive: true)
..writeAsBytesSync(encodePng(i0));
});
});
}

0 comments on commit 6a4234d

Please sign in to comment.