mbugr
mbugr

Reputation: 352

Detect if mouse move in circle way

I try to implement mouse movement tracking, When the mouse move in circle way or rectangle way show specific message.

bool IsWithinCircle(int centerX, int centerY, int mouseX, int mouseY, double radius) 
{
    int diffX = centerX - mouseX;
    int diffY = centerY - mouseY;
    return  (diffX * diffX + diffY * diffY) <= radius * radius;
}

I detect circle shape using this function using mouse location. any other way to detect the mouse movement?
Could you give a bit of sample code or a link?

Upvotes: 4

Views: 2904

Answers (2)

bdimag
bdimag

Reputation: 963

Sample code below... This will track consecutive mouse movements to determine if a circle was drawn. Each time the MouseMove event fires, the cursor Point is added to a collection and a timer is started to evaluate the collection and reset.

When evaluating the collection, it will first determine the center Point, then it will make sure that all points are within a certain distance from the center (within the green area in the image below) and that there are points in all quadrants (a full circle).

enter image description here

with a simple WPF window:

<Window x:Class="Shaping.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Canvas x:Name="ShapeCanvas" Background="Transparent" />
        <Ellipse Width="100" Height="100" Stroke="Black" StrokeThickness="1" HorizontalAlignment="Center" VerticalAlignment="Center"/>
    </Grid>
</Window>

and some messy code behind:

public partial class MainWindow : Window
{
    List<Point> points;
    Timer reset;

    public MainWindow()
    {
        InitializeComponent();

        points = new List<Point>();
        reset = new Timer { AutoReset = false, Interval = 500 };
        reset.Elapsed += (s, e) =>
        {
            App.Current.Dispatcher.BeginInvoke((Action)(() =>
            {
                ShapeCanvas.Children.Clear();
            }));

            if (points.Count > 10)
            {

                double? max_x = null;
                double? min_x = null;
                double? max_y = null;
                double? min_y = null;

                // evaulate points to determine center
                foreach (var point in points)
                {
                    max_x = Math.Max(max_x ?? point.X, point.X);
                    min_x = Math.Min(min_x ?? point.X, point.X);
                    max_y = Math.Max(max_y ?? point.Y, point.Y);
                    min_y = Math.Min(min_y ?? point.Y, point.Y);
                }
                var center = new Point((((double)max_x - (double)min_x) / 2) + (double)min_x, (((double)max_y - (double)min_y) / 2) + (double)min_y);

                var count_r = 0;
                var sum_r = 0d;
                var all_r = new List<double>();
                var quadrands = new int[4];

                // evaluate points again to determine quadrant and to get all distances and average distance
                foreach (var point in points)
                {
                    // distance
                    var r = Math.Sqrt(Math.Pow(point.X - center.X, 2) + Math.Pow(point.Y - center.Y, 2));
                    sum_r += r;
                    count_r += 1;
                    all_r.Add(r);

                    // quadrand
                    quadrands[(point.Y > center.Y ? 1 : 0) + (point.X > center.X ? 2 : 0)] += 1;
                }
                var r_avg = sum_r / count_r;

                if (all_r.Count(r => Math.Abs(r - r_avg) < r_avg * .3) >= count_r * .8 && quadrands.All(q => q > 1))
                {
                    // you made a circle
                    App.Current.Dispatcher.BeginInvoke((Action)(() =>
                    {
                        ShapeCanvas.Children.Clear();
                        ShapeCanvas.Children.Add(new Ellipse() { Fill = new SolidColorBrush(Colors.Red), Width = 15, Height = 15 });
                        reset.Start();
                    }));
                }
            }

            points.Clear();
        };

        ShapeCanvas.MouseMove += (s, e) =>
        {
            points.Add(e.GetPosition((Canvas)s));
            reset.Stop();
            reset.Start();
        };

    }
}

Note about quadrands.All(q=> q > 1) -- I was originally checking for even distribution between all quadrants quadrants.All(q=> Math.Abs(q - avg_quad) < avg_quad * .3 but that's only going to work when the circle is drawn at a constant speed (or if counts are adjusted based on speed; speed determined by distance between consecutive points)

.3 and .8 are just the numbers I found to describe roughly a circle -- translates to: at least 80% of points fall within +-15% of the average distance from the center.

Upvotes: 1

NextInLine
NextInLine

Reputation: 2204

You probably want to track the mouse movements as a series of line segments, and use those line segments to create an approximation of a circle. If the center of the circle stays relatively consistent, then the user is moving in a circle.

They key to this is that any two consecutive line segments approximate an arc of a circle. The normals from the center of to these line segments (normals are perpendicular lines) form lines that pass through the center of the circle. When you have two or more normals, you can calculate the center of the circle. If that center stays relatively consistent as the user moves the mouse around, and the angles of those normals around the center of the circle are proceeding clockwise or counter-clockwise, then you have a circle.

two consecutive line segments approximating a circle

As an example:

  1. The user moves from 100,0 to 104,2 to 106,6, giving you 2 lines
  2. The slope of a line is Δy/Δx. The slope of a normal is -Δx/Δy
  3. The normal of lines 1 starts at 102,1 and has a slope of -1/2 (atan2(-1,2) yields -26°)
  4. The normal of lines 2 starts at 105,4 and has a slope of -2/1 (atan2(-2,1) yields -63°)
  5. These lines intersect at 99,7
  6. If all the subsequent line segments give a center of 99±t,7±t, where t is some tolerance, you have a continuous arc
  7. When the user has gone consecutively through the degrees (or radians) of a circle, then they have made a circle

Some things to consider:

  • since mouse move events typically fire a lot, resulting in lots of the same value or values right next to each other, you'll have to wait until have enough movement to calculate a reasonable normal - you'll have to experiment to see what works for you
  • tolerances will also depend on what your experimentation shows works for you

Upvotes: 2

Related Questions