Skip to content

Commit 7252817

Browse files
committed
core: Fix endpoint convention for hit tests
The ray-edge intersection when calculating a shape's winding number was not consistent between straight edges and bezier curves. This causes incorrect hit testing when the test point lied on the same Y position as an edge endpoint. Now handle endpoints with the following convention: * When an edge is moving downward (+y), exclude the initial point, and include the final point * When an edge is moving downward (-y), include the initial point, and exclude the final point.
1 parent 82716d9 commit 7252817

File tree

1 file changed

+13
-13
lines changed

1 file changed

+13
-13
lines changed

render/src/shape_utils.rs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1073,14 +1073,14 @@ fn winding_number_line(
10731073
// An upward segment (-y) increments the winding number (including the initial endpoint).
10741074
// A downward segment (+y) decrements the winding number (including the final endpoint)
10751075
// Perp-dot indicates which side of the segment the point is on.
1076-
if y0 <= point_y {
1077-
if y1 > point_y {
1076+
if y0 < point_y {
1077+
if y1 >= point_y {
10781078
let val = (x1 - x0) * (point_y - y0) - (y1 - y0) * (point_x - x0);
10791079
if val > 0.0 {
10801080
return 1;
10811081
}
10821082
}
1083-
} else if y1 <= point_y {
1083+
} else if y1 < point_y {
10841084
let val = (x1 - x0) * (point_y - y0) - (y1 - y0) * (point_x - x0);
10851085

10861086
if val < 0.0 {
@@ -1107,8 +1107,8 @@ fn winding_number_curve(
11071107
// However, there are two issues:
11081108
// 1) Solving the quadratic needs to be numerically robust, particularly near the endpoints 0.0 and 1.0, and as the curve is tangent to the ray.
11091109
// We use the "Citardauq" method for improved numerical stability.
1110-
// 2) The convention for including/excluding endpoints needs to act similarly to lines, with the initial point included if the curve is "downward",
1111-
// and the final point included if the curve is pointing "upward". This is complicated by the fact that the curve could be tangent to the ray
1110+
// 2) The convention for including/excluding endpoints needs to act similarly to lines, with the initial point included if the curve is "upward",
1111+
// and the final point included if the curve is pointing "downward". This is complicated by the fact that the curve could be tangent to the ray
11121112
// at the endpoint (this is still considered "upward" or "downward" depending on the slope at earlier t).
11131113
// We solve this by splitting the curve into y-monotonic subcurves. This is helpful because
11141114
// a) each subcurve will have 1 intersection with the ray
@@ -1167,18 +1167,18 @@ fn winding_number_curve(
11671167
// Verify that this monotonic segment straddles the ray, and choose winding direction.
11681168
// This also handles the endpoint conventions.
11691169
let direction = if y_end > y_start {
1170-
// Downward edge: initial point included, increments winding.
1171-
if y_start > 0.0 || y_end <= 0.0 {
1172-
0
1173-
} else {
1170+
// Downward edge: increments winding, final point included.
1171+
if y_start < 0.0 && y_end >= 0.0 {
11741172
1
1173+
} else {
1174+
0
11751175
}
11761176
} else if y_start > y_end {
1177-
// Upward edge: final point included, increments winding.
1178-
if y_start <= 0.0 || y_end > 0.0 {
1179-
0
1180-
} else {
1177+
// Upward edge: decrements winding, initial point included.
1178+
if y_start >= 0.0 && y_end < 0.0 {
11811179
-1
1180+
} else {
1181+
0
11821182
}
11831183
} else {
11841184
0

0 commit comments

Comments
 (0)