# javascript – How to determine straight and broken mouse movements?

## Question:

How to catch in Javascript – absolutely straight and broken mouse movements, excluding curves.

## Algorithm and pseudocode

Here we will have the following entities: the `Point` class, the `Points` container, and variables of its type: `Input` – stores all recorded mouse coordinates, `Output` – will store the points of a polyline (or a segment, if a straight line is drawn).

In addition, we will need functions for finding the angle between two points `Angle.Find (Point, Point)` and the function for finding the modulus of the difference between two angles `Angle.Difference (double, double)` .

To solve the problem, we will go through all the points and find approximately even segments. And yes, the maximum deviation for the line will be in the `Angle.Eps` constant.

``````// начало ломаной
// координаты текущего отрезка
Point begin Input[0];
Point end = Input[1];
// угол для сверки
double angle = Angle.Find(begin, end);
// обработка всех точек
for (int i = 2; i < Input.Length - 1; i++) {
double currentAngle = Angle.Find(begin, Input[i]);
if (Angle.Difference(currentAngle, angle) < Angle.Max) {
end = Input[i];
//TODO: можно также высчитывать угол по всем точкам отрезка
} else {
// сохраняем старый отрезок
// создаём новый отрезок
begin = Input[i];
end = Input [++i];
angle = Angle.Find(begin, end);
}
}
``````

As a result, if all points are on one straight line, `Output` will contain only two points: the beginning and the end of the segment. If there are more of them, the line is broken. And if a broken line has a very small distance between points, it is a curved line. Checking for a curved line can be quite simple:

``````bool crooked = false;
for (int i = 1; i < Output.Length; i++) {
// функция вычисления расстояния между точками и эпсилон для длины отрезков
if (Point.Distance(Output[i], Output[i - 1]) < Point.Eps) {
crooked = true;
break;
}
}
``````

Just in case, a little clarification. I selected `Angle.Difference` because you can't just subtract angles – for angles of 1 and 359 degrees the difference is only two, not 358. It can be defined as follows:

``````double Angle::Difference (double a, double b) {
if (a > 270 && b < 90) {// 3*Pi/2, Pi/2, если используете радианы
b += 360; // 2 *Pi
} else if (b > 270 && a < 90) {
a += 360;
}
return abs(a - b);
}
``````

## Working JS version

``````// -- Вспомогательные функции --

function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}

function angleDif(a1, a2) {
if (a1 > 270 && a2 < 90) {
a2 += 360;
} else if (a2 > 270 && a1 < 90) {
a1 += 360;
}
return Math.abs(a1 - a2);
}

function angleFind(p1, p2) {
return Math.atan2(p2.y - p1.y, p2.x - p1.x) * 180 / Math.PI;
}

function distance(p1, p2) {
return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
}

// -- Инициализация --

var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');
var input = [];
var output = [];
var previous = {x:-64, y:-64};

ctx.fillStyle="#CDBCFF";
ctx.fillRect(0, 0, canvas.width, canvas.height);

// -- Константы --

// Отклонение в градусах, при котором прямая считается ломаной,
// и появляется изгиб.
SMALL_ANGLE = 9;

// Расстояние в пикселях между изгибами,
// меньше которого они считаются резкими.
BEND_DIST = 10;

// Число резких изгибов, при котором ломаная считается кривой.
NEED_BENDS = 5;

// -- Основные функции --

// Обрабатывает событие движения мыши.
var mousePos = getMousePos(canvas, evt);
var current = {x:mousePos.x, y:mousePos.y}

// не сохраняем мусорные точки
if (distance(current, previous) > 2) {
previous = {x: mousePos.x, y: mousePos.y};
input.push(previous);
process();
output.push({x: mousePos.x, y: mousePos.y});
drawResult();
}
ctx.fillStyle = "#000000";
}, false);

// Отрисовывает линию.
function drawResult() {
if (output.length >= 2) {
ctx.fillStyle="#CDBCFF";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#000000";
ctx.beginPath();
ctx.moveTo(output[1].x, output[1].y);
for (var i = 2; i < output.length; i ++) {
ctx.lineTo(output[i].x, output[i].y);
}
ctx.stroke();
}
}

// Выбирает точки для отрисовки и определяет тип линии.
function process() {
if (input.length < 2)
return;

// по этим двум точкам определяется текущее направление линии
var begin = input[0];
var last = null;

// текущая точка
var current;

// текущее направление линии
var angle = 0;
// направление к текущей точке
var curAngle = 0;

// количество резких изгибов
var bends = 0;

// список точек для отрисовки
output = [];
output.push(begin);

for (var i = 1; i < input.length; i++) {
current = input[i];

if (!last) {
if (distance(begin, current) > 2) {
last = current;
angle = angleFind(begin, last);
}
continue;
}

curAngle = angleFind(begin, current);

// находим угол последнего изгиба
if (angleDif(angle, curAngle) < SMALL_ANGLE) {
// если угол мало меняется, то перейти
// к следующей точке
last = current;

} else {
// если угол большой при малом расстоянии,
// то вероятно, что у нас кривая
if (distance(begin, current) < BEND_DIST) {
bends += 1;
}

// если угол большой, то у нас не прямая
// нужно добавить новую точку
output.push(last);
begin = current;
last = null;
}
}

// убрать "Прямая" если много точек
if (output.length > 1) {
// можно менять число перегибов нужное для кривой
if (bends >= NEED_BENDS) {
} else {
}
}
}``````
``````body {background-color:#FFF}
#myCanvas {border:1px solid #999}``````
``````<div id="answer">Прямая!</div>
<p>Проведите мышкой внутри этого прямоугольника:</p>
<canvas id="myCanvas" width="578" height="200"></canvas> ``````
Scroll to Top