I have a problem with calculating the sign of angles between 3 points. The head of my dataframe looks like this:
x y z
6554 158.485 170.917 177.917
6228 159.138 171.466 179.099
6698 159.088 170.504 180.273
1833 157.945 169.852 180.449
1875 157.720 168.950 181.571
1893 156.800 167.829 181.119
I made a loop to calculate the angle between them by using the a.b = |a|*|b|*cos(alpha). Unfortunatelly with this, I can't get sign, because the acos function will give me answers only from [0,pi] interval. This is my loop:
for(kh in 1:nrow(CA_list)) {
CA_list[kh,4] <- CA_list[kh,1]-CA_list[kh+1,1]
CA_list[kh,5] <- CA_list[kh,2]-CA_list[kh+1,2]
CA_list[kh,6] <- CA_list[kh,3]-CA_list[kh+1,3]
}
colnames(CA_list)[4] <- "vecx"
colnames(CA_list)[5] <- "vecy"
colnames(CA_list)[6] <- "vecz"
CA_list$abs <- sqrt(CA_list$vecx^2+CA_list$vecy^2+CA_list$vecz^2)
for(kg in 1:nrow(CA_list)) {
CA_list[kg,8] <- CA_list[kg,4]*CA_list[kg+1,4]+CA_list[kg,5]*CA_list[kg+1,5]+CA_list[kg,6]*CA_list[kg+1,6]
CA_list[kg,9] <- CA_list[kg,7]*CA_list[kg+1,7]
}
colnames(CA_list)[8] <- "LHS"
colnames(CA_list)[9] <- "RHS"
CA_list$angles <- acos(CA_list$LHS/CA_list$RHS)
the dataframe will look like this:
x y z vecx vecy vecz abs LHS RHS angles
6554 158.485 170.917 177.917 -0.653 -0.549 -1.182 1.457715 0.826880 2.213722 68.06684
6228 159.138 171.466 179.099 0.050 0.962 -1.174 1.518624 0.890998 2.016130 63.77260
6698 159.088 170.504 180.273 1.143 0.652 -0.176 1.327603 1.042751 1.934437 57.38127
1833 157.945 169.852 180.449 0.225 0.902 -1.122 1.457091 0.710998 2.213313 71.26225
1875 157.720 168.950 181.571 0.920 1.121 0.452 1.518995 0.884453 2.016499 63.98489
1893 156.800 167.829 181.119 -0.401 1.233 -0.285 1.327522 1.067264 1.932921 56.48529
Now I want complete my loop with some function that can calculate the signs of the angles. What would be the easiest method for me?
The signed angle between two 2D-vectors u
and v
can be obtained with the help of the atan2
function:
angle_unsigned <- function(u, v){
acos( sum(u*v) / ( sqrt(sum(u*u)) * sqrt(sum(v*v)) ) )
}
angle_signed <- function(u, v){
atan2(v[2], v[1]) - atan2(u[2], u[1])
}
In 3D, you need to have a direction, represented by a vector n
. The angle3D_signed
function below returns the angle in [0,2pi[
by which the vector u
must rotate counterclockwise, as seen from the direction defined by n
, to reach the vector v
.
crossProduct <- function(v, w){
c(
v[2] * w[3] - v[3] * w[2],
v[3] * w[1] - v[1] * w[3],
v[1] * w[2] - v[2] * w[1]
)
}
angle3D_signed <- function(n, u, v){
n <- n / sqrt(sum(n*n)) # normalize n
unorm <- sqrt(sum(u*u))
vnorm <- sqrt(sum(v*v))
s <- sum(crossProduct(n, u) * v) # "unnormalized sine"
cosAngle <- sum(u*v) / unorm / vnorm
ifelse(s >= 0, acos(cosAngle), 2*pi - acos(cosAngle))
}