Finding a "movement direction" (angle) of a point
Asked Answered
B

2

8

I'm working on a pretty cool project where I'm collecting data about the movement of a cursor, but I've run into an issue where I think I could use some help. I am constantly reading in data about the x and y position of the cursor (along with other relevant data), and once the cursor exceeds a certain threshold in the y-dimension, I need to calculate the movement direction (angle). Let me illustrate with a figure I drew:

enter image description here

What tends to happen is that the cursor moves in a somewhat straight line, but then curves towards the end of the movement. I need to calculate theta, i.e., the angle of the blue vector with respect to the positive x-axis. The idea I came up with is to use the last 2 samples to largely determine what the movement direction is, otherwise if I use too many samples I would skew what the actual angle is. To give an extreme case let me follow up with another picture:

enter image description here

Here each dot represents a sample. Note that if I use BOTH dots, the real angle I want will be wrong (again, I need to find the direction the cursor was moving in last, which is the vector drawn at the end of the line). I dont expect this case to come up much, but was wondering if there would be a way to solve for it if it does.

Lastly, note that the these motions can occur in either the first or second quadrant, if that makes a difference.

I'd really appreciate any help here. I'm coding this in C++ but I think I could translate any answer. Thanks.

Biplane answered 31/7, 2015 at 18:14 Comment(6)
You need a minimum of two points to find direction. Unless you are sampling very finely you cannot avoid this problem.Quartan
direction of the movement cannot be different from the calculated direction between the last two points, IF you use the resolution of the cursor. But if the resolution you are using for calculation is less than the resolution of the cursor (movement of the cursor) then the second figure can happen.Leede
That makes sense. Can you help me solve the first case then, using two points? What if I wanted to use more than 2? I dont think I will, I'm actually just interested.Biplane
the angle in radians is equal to atan2(y2-y1, x2-x1).Teammate
If you want to determine direction with more than 2 points, you create for yourself a line-fitting problem. Lots of ways to solve, none trivial.Phillips
with more than 2 points I would use interpolation polynomial and compute the angle from its derivation on the endpoint ... for example here interpolation cubic then compute 2 points at the end (with predefined distance to each other) and compute the angle by atan2 ... Also the control points I would remember by min distance not all of the samples ... so if cursor moves more then min limit shift control points otherwise just update position of the last control point ...Osteoarthritis
F
3

This should get you started http://jsfiddle.net/0ao9oa7a/

  • Get all of the recorded points
  • Filter out points that are close together (I use 5 pixels)
  • Find the angles of each consecutive pair of points (atan2)
  • Find the absolute differences between each consecutive pair of angles
    • Throw away all of the angles before the max difference
  • Average the remaining angles (average all point vectors then atan2 back into an angle)

Code

function process(points) {
    if(points.length === 0) { 
        txt = "Not enough points\n" + txt;
        return null; 
    }
    // compress points, removing those that are too close together
    var newPoints = [];
    newPoints.push(points[0]);
    for(var i = 1; i < points.length; i++) {
        if(Math.sqrt(Math.pow(points[i].x - newPoints[newPoints.length - 1].x, 2) + Math.pow(points[i].y - newPoints[newPoints.length - 1].y, 2)) > 5) {
            newPoints.push(points[i]);
        }
    }
    points = newPoints;
    if(points.length < 2) { 
        txt = "Not enough points\n" + txt;
        return null; 
    }
    // get all of the angles
    var angles = [];
    for(var i=0; i < points.length - 1; i++) {
        var rad = Math.atan2(points[i + 1].y - points[i].y, points[i + 1].x - points[i].x);
        angles[i] = rad;
        txt += "x: " + (points[i].x|0) + " y: " + (points[i].y|0) + " x: " + (points[i+1].x|0) + " y: " + (points[i+1].y|0) + " [" + ((rad * 180 / Math.PI)|0) + "]" + "\n";
    }
    txt += "\n";
    // get all of the diffs between angles
    // save the index of the max diff
    var absDiffs = [];
    var maxDiff = -1;
    var maxDiffAngleIndex = -1;
    for(var i=0; i < points.length - 1; i++) {
        var delta = Math.abs(angles[i] - angles[i + 1]);
        if(delta >= maxDiff) {
            maxDiff = delta;
            maxDiffAngleIndex = i + 1;
        }
    }
    if(maxDiffAngleIndex == -1) {
        txt = "Angle: " + angles[0] + " : " + (angles[0] * 180 / Math.PI) + "\n" + txt;
        return angles[0];
    } else if(maxDiffAngleIndex == angles.length - 1) {
        txt = "Angle: " + angles[angle.length - 1] + " : " + (angles[angles.length - 1] * 180 / Math.PI) + "\n" + txt;
        return angles[angles.length - 1];
    } else {
        // find the average angle from the index to the end
        var sumX = 0;
        var sumY = 0;
        for(var i = maxDiffAngleIndex; i < angles.length; i++) {
            sumX += Math.cos(angles[i]);
            sumY += Math.sin(angles[i]);
        }
        var avgX = sumX / (angles.length - maxDiffAngleIndex);
        var avgY = sumY / (angles.length - maxDiffAngleIndex);
        //
        var avgAngle = Math.atan2(avgY, avgX);
        txt = "Angle: " + avgAngle + " : " + (avgAngle * 180 / Math.PI) + "\n" + txt;
        return avgAngle;
    }
}
Foreshank answered 31/7, 2015 at 19:57 Comment(0)
B
0

As I can see, the “movement direction” (angle) of a point would be the angular coefficient of two dots, one dot at the end of the vector and the other one at the begnning.

Cause we can only find the angle with two dots, so we can make a line, since the direction vector would be (B-A), where A and B are the points I already told you about.

We can calcule this using the formula of the angular coefficient of a line:

m = Tan θ = Δy / Δx

And that is simply:

Tan θ = (yB – yA) / (xB – xA)

Where θ is the “movement direction” (angle) and (x,y) are the coordinates of the points A and B.

Talking about the quadrant, you will only need to use the trigonometric circle to know the sinal of the value of Tan θ, so take a look at this image:

enter image description here

And of course, after you find the value of Tan θ, you will need to use it to find the arctan θ, and that will be your final answer.

Bourse answered 31/7, 2015 at 20:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.