Detect Clicks Inside Polygons on Canvas with Crossing & Winding Numbers
Learn how to capture click events on an HTML5 Canvas, compute the click coordinates, and determine whether the point lies inside a polygon using both the Crossing Number (even‑odd) method and the more robust Winding Number algorithm, complete with JavaScript code examples.
In Canvas‑based applications, clicking a shape often needs to trigger interaction, but Canvas does not support attaching events directly to drawn elements, so mathematical techniques are required.
Getting coordinates
Although Canvas cannot add events to individual shapes, you can listen to the Canvas element’s click event and calculate the click position relative to the canvas.
<code>canvas.addEventListener('click', (event) => {
const rect = canvas.getBoundingClientRect();
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
const point = { x, y };
console.log(point); // prints click coordinates
});</code>If the user clicks inside a shape, the click point will lie within that shape. For simplicity we discuss polygons only. Determining whether a point is inside a polygon can be done with the Crossing Number (even‑odd) method or the Winding Number method.
Crossing Number Method
The crossing number method casts a ray from the point; if the ray intersects the polygon edges an odd number of times, the point is inside, otherwise it is outside.
Typically a horizontal ray to the right (positive x‑axis) is used. The implementation is:
<code>function getCrossingNumber(point, lines) {
let count = 0;
for (let i = 0; i < lines.length; i++) {
// o, d are the start and end points of a polygon edge
const { o, d } = lines[i];
// an intersection occurs only when the edge straddles the horizontal ray
if ((o.y > point.y) ^ (d.y > point.y)) {
const x = (point.y - o.y) * (d.x - o.x) / (d.y - o.y) + o.x;
if (x > point.x) {
count += 1;
}
}
}
return count;
}
</code>The Fabric.js library uses this crossing‑number technique to test point‑in‑polygon (see its source code).
In some cases the crossing‑number method can give incorrect results; the winding‑number method is generally more reliable.
Winding Number Method
The winding number method also casts a horizontal ray to the right. When an edge crosses the ray upward, the winding number increments; when it crosses downward, the winding number decrements. A non‑zero winding number means the point is inside.
For humans, distinguishing upward versus downward crossings is intuitive, but in code it is easier to convert this vertical relationship into a left/right test. If an edge crosses upward, the point P lies to the left of edge AB; if it crosses downward, P lies to the right.
Using the right‑hand rule, when P is on the left side of AB the outward normal of AB and AP points outward; when P is on the right side, the normal points inward. The normal can be computed via the cross product of vectors AB and AP:
<code>vector AB = (x1, y1, 0)
vector AP = (x2, y2, 0)
AB × AP = (x1*y2 - x2*y1)k
// k is the unit vector along the z‑axis; the sign of (x1*y2 - x2*y1) indicates direction
</code>From this we obtain:
If x1*y2 - x2*y1 > 0, P is on the left side of AB, winding number +1.
If x1*y2 - x2*y1 = 0, P lies on AB, winding number unchanged.
If x1*y2 - x2*y1 < 0, P is on the right side of AB, winding number -1.
Summary
Both algorithms allow you to determine whether a click on a Canvas falls inside a polygon and then trigger the appropriate interaction. The winding‑number method requires a bit more mathematical insight but is straightforward to implement.
References
https://blog.csdn.net/u013279723/article/details/106265948/
https://www.geeksforgeeks.org/even-odd-method-winding-number-method-inside-outside-test-of-a-polygon/
https://towardsdatascience.com/is-the-point-inside-the-polygon-574b86472119
KooFE Frontend Team
Follow the latest frontend updates
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.