From 6a4234d2f37858b019c849eae19876c857d7680f Mon Sep 17 00:00:00 2001 From: Brendan Duncan Date: Fri, 20 Dec 2024 15:39:06 -0700 Subject: [PATCH] Fix fillPolygon to better handle concave polygons --- lib/src/draw/fill_polygon.dart | 71 ++++++++------------------------ test/draw/fill_polygon_test.dart | 21 +++++++++- 2 files changed, 37 insertions(+), 55 deletions(-) diff --git a/lib/src/draw/fill_polygon.dart b/lib/src/draw/fill_polygon.dart index 994b0db4..cb9ca11f 100644 --- a/lib/src/draw/fill_polygon.dart +++ b/lib/src/draw/fill_polygon.dart @@ -64,65 +64,28 @@ Image fillPolygon(Image src, xMax = min(xMax, src.width - 1); yMax = min(yMax, src.height - 1); - final inter = List.filled(40, 0); - final vi = - List.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; } diff --git a/test/draw/fill_polygon_test.dart b/test/draw/fill_polygon_test.dart index 5aab9ddd..e6415d49 100644 --- a/test/draw/fill_polygon_test.dart +++ b/test/draw/fill_polygon_test.dart @@ -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(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)); + }); }); }