Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rotate graph by angle

I have multiple matrices filled with the x and y coordinates of multiple points in 2D space that make up a graph. The matrices look something like this

x1 x2 x3 x4 ...
y1 y2 y3 y4 ...

A possible graph looks something like this

enter image description here

What I want to do is rotate the graph around point A so that the line between the points A and B are parallel to the X-Axis.

My idea was to treat the line AB as the hypothenuse of a right-triangle, calculate α (the angle at point A) and rotate the matrix for this graph by it using a rotation matrix.

What I did so far is the following

#df is the subset of my data that describes the graph we're handling right now,
#df has 2 or more rows

beginx=df[1,]$xcord          #get the x coordinate of point A
beginy=df[1,]$ycord          #get the y coordinate of point A
endx=df[nrow(df)-1,]$xcord   #get the x coordinate of point B
endy=df[nrow(df)-1,]$ycord   #get the y coordinate of point B
xnow=df$xcord
ynow=df$ycord
xdif=abs(beginx-endx)
ydif=abs(beginy-endy)




 if((xdif != 0) & (ydif!=0)){
     direct=sqrt(abs((xdif^2)-(ydif^2))) #calculate the length of the hypothenuse
     sinang=abs(beginy-endy)/direct      
     angle=1/sin(sinang)
     if(beginy>endy){
     angle=angle
 }else{
     angle=360-angle
 }
rotmat=rot(angle)    # use the function rot(angle) to get the rotation matrix for
                         # the calculated angle
A = matrix(c(xnow,ynow),nrow=2,byrow = TRUE)  # matrix containing the graph coords
admat=rotmat%*%A                          #multiply the matrix with the rotation matrix
}

This approach fails because it isn't flexible enough to always calculate the needed angle with the result being that the graph is rotated by the wrong angle and / or in the wrong direction.

Thanks in advance for reading and hopefully some of you can bring some fresh ideas to this

Edit: Data to reproduce this can be found here

X-Coordinates

Y-Coordinates

Not sure how to provide the data you've asked for, I'll gladly provide it in another way if you specify how you'd like it

like image 822
deemel Avatar asked Dec 05 '22 11:12

deemel


2 Answers

Like this?

#read in X and Y as vectors
M <- cbind(X,Y)
#plot data
plot(M[,1],M[,2],xlim=c(0,1200),ylim=c(0,1200))
#calculate rotation angle
alpha <- -atan((M[1,2]-tail(M,1)[,2])/(M[1,1]-tail(M,1)[,1]))
#rotation matrix
rotm <- matrix(c(cos(alpha),sin(alpha),-sin(alpha),cos(alpha)),ncol=2)
#shift, rotate, shift back
M2 <- t(rotm %*% (
  t(M)-c(M[1,1],M[1,2])
  )+c(M[1,1],M[1,2]))
#plot
plot(M2[,1],M2[,2],xlim=c(0,1200),ylim=c(0,1200))

enter image description here

Edit:

I'll break down the transformation to make it easier to understand. However, it's just basic linear algebra.

plot(M,xlim=c(-300,1200),ylim=c(-300,1200))
#shift points, so that turning point is (0,0)
M2.1 <- t(t(M)-c(M[1,1],M[1,2]))
points(M2.1,col="blue")
#rotate
M2.2 <- t(rotm %*% (t(M2.1)))
points(M2.2,col="green")
#shift back
M2.3 <- t(t(M2.2)+c(M[1,1],M[1,2]))
points(M2.3,col="red")

enter image description here

like image 194
Roland Avatar answered Dec 16 '22 15:12

Roland


Instead of a data frame, it looks like your data is better served as a matrix (via as.matrix).

This answer very similar to Roland's, but breaks things down into more steps and has some special-case handling when the angle is a multiple of pi/2.

#sample data
set.seed(1) #for consistency of random-generated data
d <- matrix(c(sort(runif(50)),sort(runif(50))),ncol=2)

#rotation about point A
rotA <- function(d) {
d.offset <- apply(d,2,function(z) z - z[1]) #offset data
  endpoint <- d.offset[nrow(d.offset),] #gets difference
  rot <- function(angle) matrix(
    c(cos(angle),-sin(angle),sin(angle),cos(angle)),nrow=2) #CCW rotation matrix
  if(endpoint[2] == 0) {
    return(d) #if y-diff is 0, then no action required
  } else if (endpoint[1] == 0) { 
    rad <- pi/2 #if x-diff is 0, then rotate by a right angle
  } else {rad <- atan(endpoint[2]/endpoint[1])}
  d.offset.rotate <- d.offset %*% rot(-rad) #rotation
  d.rotate <- sapply(1:2,function(z) d.offset.rotate[,z] + d[1,z]) #undo offset
  d.rotate
}

#results and plotting to check visually
d.rotate <- rotA(d)
plot(d.rotate)
abline(h=d[1,2])
like image 37
Blue Magister Avatar answered Dec 16 '22 16:12

Blue Magister