How to find angle between two straight lines (paths) on a SVG in Javascript?
Asked Answered
S

4

11

I have two straight lines as <path> in SVG canvas. Using pixel coordinates of LineA (A1x, A1y) (A2x, A2y) and LineB (B1x, B1y) (B2x, B2y) how can I calculate the angle between these lines.

I have below code which works for THREE points (it works for green cases in below image). It does not work when (A2x, A2y) != (B1x, B1y).

How can I modify this formula to work even when lines are not joined.

function find_angle(p0,p1,c) {
var p0c = Math.sqrt(Math.pow(c.x-p0.x,2)+
                    Math.pow(c.y-p0.y,2));  
var p1c = Math.sqrt(Math.pow(c.x-p1.x,2)+
                    Math.pow(c.y-p1.y,2));
var p0p1 = Math.sqrt(Math.pow(p1.x-p0.x,2)+
                     Math.pow(p1.y-p0.y,2));
var angle = Math.acos((p1c*p1c+p0c*p0c-p0p1*p0p1)/(2*p1c*p0c));
return angle * (180 / Math.PI);
}

Image

Seasick answered 10/2, 2017 at 12:8 Comment(2)
Vectors in 2D will give you this answer easily using either dot or cross product. I'd prefer the latter, because you have two angles to choose from. The cross product lets you choose which one by specifying the out-of-plane direction.Multipartite
Your current function gives you the same results whether you call with (p0,p1,c) or (p1,p0,c). Is that really what you want?Burson
E
19

You can exploit Math.atan2 function with cross product and dot product of direction vectors for these segments. Note the atan2 returns signed angle in range -Pi...Pi

//find vector components
var dAx = A2x - A1x;
var dAy = A2y - A1y;
var dBx = B2x - B1x;
var dBy = B2y - B1y;
var angle = Math.atan2(dAx * dBy - dAy * dBx, dAx * dBx + dAy * dBy);
if(angle < 0) {angle = angle * -1;}
var degree_angle = angle * (180 / Math.PI);
Extraterrestrial answered 10/2, 2017 at 12:14 Comment(0)
S
1

You want P0-C, P1-D instead of P0-C, P1-C: just translate one of the segments to let D and C coincide: P1' = P1 - D + C (then D' = C).

Salicin answered 10/2, 2017 at 13:35 Comment(1)
I realized after submitting my answer that I used the same approach that you advocate here in more conceptual language.Burson
J
0

finding the angle (@) between two lines:

tan@ = (m1-m2)/(1+m1.m2)

where m1 and m2 are the gradient of the lines respectively. In terms of JS:

var m1 = (A1y-A2y)/(A1x-A2x)
var m2 = (B1y-B2y)/(B1x-B2x)
var angle
if(m1*m2==-1){
    angle = Math.PI/2
}else{
    angle = Math.atan((m1-m2)/(1+m1*m2))
}
Jodijodie answered 10/2, 2017 at 12:17 Comment(0)
B
0

After submitting my answer, I realize this is the same solution as the one provided by @YvesDaoust. That answer is a more concise conceptual summary of the same approach I have fleshed out here with a JavaScript example.

The answer is fairly simple:

function find_disconnected_angle(p0,c0, p1,c1) {
  return find_angle({x:p0.x-c0.x+c1.x,y:p0.y-c0.y+c1.y},p1,c1);
}

You can calculate the angle from scratch using trigonometry fundamentals. However, to make your life easier you can also just use the function you already have. First, just mathematically translate one line so that one of its end points coincides with one of the end points of the other line. There are four different ways of matching up one end point from each line, and each way will produce a potentially different angle measure. However, this is no bigger of a dilemma than you having to figure out which angle you want of the four angles you get when you take each of the original untranslated line segments, extend each into an infinite line, and examine the four angles where those two lines intersect.

You need a function that takes 4 points as inputs, i.e. p0, p1, p2 and p3. However, just to make clear which points are being made coincidental, I've instead labeled them as p0, c0, p1 and c1, such that p0 and c0 are both being moved in such a way as to make c0 and c1 coincide, resulting in three points: p0new, p1 and c, the latter of which equals both c1 and c0new.

Update: Upon examining your original function more closely, I realize my discussion above of the choice of four possible angles may not be relevant with the exact function implementation you have written, as the order of points p0 and p1 does not matter for your function. You could rewrite your original function, perhaps using some of the concepts from the other answers, to be able to more fully control which angle you get as a result, if that's really what you want. In any case, the general concept behind my answer holds: if you already have a function that calculates an angle between 3 points (with whatever limitations the algorithm has), you can use the same function on two disconnected line segments by simply translating one so that two end points coincide and then using the same function (again, with whatever limitations the algorithm still has).

function find_angle(p0,p1,c) {
  var p0c = Math.sqrt(Math.pow(c.x-p0.x,2)+
                      Math.pow(c.y-p0.y,2));  
  var p1c = Math.sqrt(Math.pow(c.x-p1.x,2)+
                      Math.pow(c.y-p1.y,2));
  var p0p1 = Math.sqrt(Math.pow(p1.x-p0.x,2)+
                       Math.pow(p1.y-p0.y,2));
  var angle = Math.acos((p1c*p1c+p0c*p0c-p0p1*p0p1)/(2*p1c*p0c));
  return angle * (180 / Math.PI);
}

function find_disconnected_angle(p0,c0, p1,c1) {
  return find_angle({x:p0.x-c0.x+c1.x,y:p0.y-c0.y+c1.y},p1,c1);
}

console.log(
  find_angle(
    {x: 7, y: 2},
    {x: 7, y: 7},
    {x: 2, y: 2}
  )
);

console.log(
  find_disconnected_angle(
    {x: 27, y: 42},
    {x: 22, y: 42},
    {x:  7, y:  7},
    {x:  2, y:  2}
  )
);
Burson answered 10/2, 2017 at 15:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.