diff --git a/src/shaders/hatch.wgsl b/src/shaders/hatch.wgsl index 5f8752e91..f9569d96b 100644 --- a/src/shaders/hatch.wgsl +++ b/src/shaders/hatch.wgsl @@ -119,19 +119,34 @@ fn in_polygon(p: vec2) -> bool { var inside = false; let n = h.vcount; var prev = vec2(0.0, 0.0); + var first = vec2(0.0, 0.0); var have_prev = false; for (var i = 0u; i < n; i++) { let vi = b.verts[i].xy; if !valid_vertex(vi) { + // Close the sub-loop that just ended (last → first edge). An + // unclosed boundary — e.g. a SOLID's 4 corners, which are not + // repeated — otherwise miscounts crossings and the fill bleeds + // outside the shape. (#140) + if have_prev && edge_crosses(p, prev, first) { + inside = !inside; + } have_prev = false; continue; } - if have_prev && edge_crosses(p, prev, vi) { - inside = !inside; + if have_prev { + if edge_crosses(p, prev, vi) { + inside = !inside; + } + } else { + first = vi; } prev = vi; have_prev = true; } + if have_prev && edge_crosses(p, prev, first) { + inside = !inside; + } return inside; } diff --git a/src/shaders/hatch_batched.wgsl b/src/shaders/hatch_batched.wgsl index 2e4912630..49023c6e4 100644 --- a/src/shaders/hatch_batched.wgsl +++ b/src/shaders/hatch_batched.wgsl @@ -150,19 +150,34 @@ fn edge_crosses(p: vec2, a: vec2, c: vec2) -> bool { fn in_polygon(p: vec2, offset: u32, count: u32) -> bool { var inside = false; var prev = vec2(0.0, 0.0); + var first = vec2(0.0, 0.0); var have_prev = false; for (var i = 0u; i < count; i++) { let vi = boundary[offset + i].xy; if !valid_vertex(vi) { + // Close the sub-loop that just ended (last → first edge). An + // unclosed boundary — e.g. a SOLID's 4 corners, which are not + // repeated — otherwise miscounts crossings and the fill bleeds + // outside the shape. (#140) + if have_prev && edge_crosses(p, prev, first) { + inside = !inside; + } have_prev = false; continue; } - if have_prev && edge_crosses(p, prev, vi) { - inside = !inside; + if have_prev { + if edge_crosses(p, prev, vi) { + inside = !inside; + } + } else { + first = vi; } prev = vi; have_prev = true; } + if have_prev && edge_crosses(p, prev, first) { + inside = !inside; + } return inside; }