Search code examples
c++algorithmmatlabmathangle

Finding a "movement direction" (angle) of a point


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.


Solution

  • 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;
        }
    }