1

Using a polygon area calculation, it's incredibly simple to determine if a buffer of x,y mouse coords is technically, mathematically moving clockwise, or counterclockwise:

let dir = Math.sign(coords.reduce((prev, coord, i) => prev +
    (coords[i+1].x - coord.x) *
    (coords[i+1].y + coord.y),0
))

But that simple code will always return either clockwise or counterclockwise, which isn't practically what ought to be the correct result.

Rapid up-and-down, or left-and-right motions, or very approximately straight lines, may technically be moving clockwise, but aren't moving clockwise in the common understanding of clockwise - there isn't an option for "NEITHER".

Is there a way to adjust that calculation to quickly determine if the 'clockwise-ness' is beyond a certain threshold?

ouflak
  • 2,458
  • 10
  • 44
  • 49
queviva
  • 11
  • 2

2 Answers2

0

Well, what you can do is

  • find the direction of the movement by interpolating the points (check any linear regression algorithm)
  • find the angle of the interpolated line
  • rotate the points by that angle (clockwise)
  • decrease the points by the intercept
  • check if any of the points has |y| > Eps where Eps is the threshold that you consider to discriminate rotating movement or straight lines

To do so, you can for example have a look here https://dracoblue.net/dev/linear-least-squares-in-javascript/ and you will see that at some point there are m (angle) and b (intercept)

function rotate(x, y, angle) {
    var radians = (Math.PI / 180) * angle,
        cos = Math.cos(radians),
        sin = Math.sin(radians),
        nx = (cos * x) + (sin * y),
        ny = (cos * y) - (sin * x);
    return [nx, ny];
}

// supposing yourPoints = [[x0,y0], [x1,y1], ...]
function checkStraight(yourPoints, epsilon = 1)

    /* code to get m and b from the link or any linear regression you prefer */

    // get radiants
    const angle = Math.atan(m); 

    // rotate by angle and decrease by intercept
    const translatedPoints = yourPoints.map(p => rotate(p[0], p[1] - b)) 

    return translatedPoints.every(p => p[1] < epsilon)
}
Alberto Sinigaglia
  • 12,097
  • 2
  • 20
  • 48
0

I'm not sure how to change that polygon area calculation to include its own threshold, but you could compare the abs() of the area of a bounding circle and set a minimum percent.

Find the bigger value between the min/max of the x and min/max of the y, and multiply by pi/4.

let circ = Math.PI/4 * Math.max(
    Math.max([...coords.x]) - Math.min([...coords.x]),
    Math.max([...coords.y]) - Math.min([...coords.y])
);

let dir = (Math.abs(area) / circ > 50%) ? Math.sign(area) : 0;

This will produce -1 or +1 for clockwise motions that sweep out an area with more than "fifty percent approximate roundness" and ZERO for motions that are too oblique to be considered clockwise motions.

In practice, you'll find that the max(X) - min(X) can be avoided by just using the difference between the first and last points in the coordinate buffer.

let diameter = Math.max(
    Math.abs(coords[0].x - coords.at(-1).x),
    Math.abs(coords[0].y - coords.at(-1).y)
);

Because mousemove capturing happens fast enough for it to not be noticeable. Furthermore, you now have an extra piece of information, the diameter, which you can use to determine 'how big' the clockwise motions are.

This can be done without any trig functions and with only one more line of code.

ouflak
  • 2,458
  • 10
  • 44
  • 49
queviva
  • 11
  • 2