Is it possible to check if the stroke drawn by the user on a canvas is a circle shape or not?
I have two arrays
y, which have all the coordinates of the stroke. How can I use this information to check if the stroke is a circle shape or not?
Best How To :
I guess that depends on your definition of "circle". It's unlikely a user will draw an actual circle, though they might come close. So first, you need to define a specification for "circle".
Here's one that might work:
- The drawn shape is a closed curve. I.e. the final point is relatively close to the initial point.
- The distance from every point to the "center", where "center" is defined to be the average of all points, is relatively close to the average distance over all points.
Note that that specification is still fairly vague. I use the phrase "relatively close" instead of precise terms, because I don't really know what your criteria for "circle" is. But I would expect the above to be a good starting point.
As noted in the comments, the above will still allow some fairly sloppy drawings to qualify as a circle, such as the "D" shape mentioned. If it's desired to limit the drawings to something reasonably round, but still permit a fairly "squashed" shape (e.g. more elliptical, egg-shaped, etc.), one can refine the above by adding a heuristic based on the angles made by triplets of points.
To do this correctly is somewhat involved. You can easily determine the angle between two vectors by using the dot product. But unfortunately, that calculation is indifferent to which vector is "first". That is, if you care about a concave vs a convex curve, dot product doesn't differentiate. The alternative is to use the
Math.Atan2() function. For example, let's look at the angle between the first three points:
// Note: normally one would make both vectors have the same start
// point, i.e. using pointList, for the subtraction of the
// Atan2 value to give the correct result. But here, what we really
// want to know is how different in direction the second vector is
// from the first, so calculating both line segment angles in the
// same direction of drawing gives a more useful result.
double vx1 = pointList.X - pointList.X, vy1 = pointList.Y - pointList.Y,
vx2 = pointList.X - pointList.X, vy2 = pointList.Y - pointList.Y;
double angleDifference = Math.Atan2(vx2, vy2) - Math.Atan2(vx1, vy1);
(You'll of course do these calculations in a loop, to get differences for each triplet of points; the above is just for illustrative purposes).
Assuming the user has drawn a circle in a counter-clockwise direction, then the above should return positive angle differences near 0. If they are too close to 0, then you will want to reject the drawing, because that suggests the user is drawing straight lines (e.g. the vertical bar of a "D"). But you'll also want to reject the drawing if some difference is too far from 0, because that suggests the user may be drawing sharp corners (e.g. the upper-left or lower-left corner of a "D").
Note: smaller circles will require sharper angles. You may want to dynamically adjust the limits used for this based on the nominal diameter of the circle (i.e. the average of the distances of each point from the center), setting the min and max angle difference smaller for larger circles, and larger for smaller circles.
Note: the angle values here are in radians. So when setting your lower and upper limits for the angle difference, you'll need to make sure you are using the correct units for the comparison.
Note: the user may of course draw a circle in the clockwise direction instead. If you sum all of the differences, you'll wind up with either a positive or negative number; that sign will tell you whether you should be comparing your differences to your desired limits, or to the negative of those limits. To address this, you can do any of the following:
- Do two passes through the data, first to find out whether you want the positive or negative values for your limits, and second to then compare the actual values to the limits.
- Save the angle calculation during the first pass, and then use those saved values for the second pass.
- Just keep track of whether your comparisons were always within the positive limits, the negative limits, or both (i.e. two flags, set the appropriate one to
true based on a comparison with both positive and negative limits). After a single pass through the data, you will know whether the positive limits or the negative limits should have been used, and then as long as only one flag is set at the end, the circle is valid (assuming no angle exceeded the absolute limits, of course).