library(animation)


Linear Transformations on a Generic Graphic


This exercise builds a graphic using a matrix or table of coordinates that define smaller rectangles.


Then it applies a 2X2 transformation matrix to alter the graphic in several ways. It displays the animations in an X Window.


The X11 plots are drawn with fig.show=‘hide’ and only the packaged animation is shown here.


Animation of all Transformations




Constants.


DEPTH_OF_DOTS<-50         # number of plot points within a unit
SLEEP_SECONDS<-0          # how long to sleep between frames
dev.control('enable')

myani=ani.record(reset = TRUE, replay.cur = FALSE)

Generic functions.


These 2 functions create arrays of x,y points that populate any box within a nXn grid, based on 4 parameters.

# Every box can be built with 4 parameters, these 2 functions share parameters since they need to be conformable


pop_x_array<-function(beg,end,x_axis_repeats, y_axis_repeats) {
  
  return(c(rep(seq(beg,end,length.out = x_axis_repeats),y_axis_repeats)))
}


# the x_axis_repeats is imortant bcuz every x point needs a y point

pop_y_array<-function(beg,end,x_axis_repeats, y_axis_repeats) {
  
 return(sort(rep(seq(beg,end,length.out = y_axis_repeats),x_axis_repeats)))
}
plot_it <-function(x_array,y_array, main_title) {
  Sys.sleep(SLEEP_SECONDS)
  plot(x_array,y_array, xlim = c(-1,1), ylim = c(-1,1), main=main_title)
  ani.record()

}


Below are 2 transformation algorithms.


The first uses the R %*% operator to perform a dot product operation.


The second does the same thing iteratively and explicitly.


plot_transformation<-function(s,x_array,y_array, main_title) {

  rbind_array<-rbind(x_array, y_array)

  new_matrix<-s%*%rbind_array
  x_new<-new_matrix[1,]
  y_new<-new_matrix[2,]
  
  plot_it(x_new,y_new, main=main_title)
}



plot_transformation2<-function(s,x_array,y_array, main_title) {

  # get the abcd of the transformation matrix
a<-s[1,1]
b<-s[1,2]
c<-s[2,1]
d<-s[2,2]


# create empty vectors
x_new<-c()
y_new<-c()

for(i in 1:length(x_array)) {
  x_pt<-x_array[i]            # get the x,y points
  y_pt<-y_array[i]
  
   
  x_pt_new<-a * x_pt + b * y_pt
  y_pt_new<-c * x_pt + d * y_pt
  x_new<-append(x_new,x_pt_new)
  y_new<-append(y_new,y_pt_new)
  
}

  plot_it(x_new,y_new, main=main_title)

}


This one plots the rotation. It accepts a theta in degrees.


It converts it to radians to create the trig rotation matrix.


Then it invokes plot_transformation()


plot_rotated<-function(degrees,x,y) {
  
  theta<-degrees*pi/180
  a<-cos(theta)
  b<-sin(theta)*-1
  c<-sin(theta)
  d<-cos(theta)

  s<-matrix(c(a, b, c, d), byrow = TRUE, ncol = 2)

  plot_transformation(s,x,y, "Rotation")

}


Build the Graphic


We build a graphic by constructing seperate rectangles.


Set up all the x,y points that define the smaller rectangles.


x_y_points<-c(0 , 0.35 , 0.8 , 0.65, 32,     # top of T
      0.15 , 0.2 , 0 , 0.65, 200,            # bottom of T
      0.65 , 0.9 , 0.8 , 0.64, 200,           # top of B
      0.65 , 0.9 , 0.48 , 0.32, 30,              # middle of B
      0.65 , 0.9 , 0.16 , 0, 30,
      0.65 , 0.7 , 0.64 , 0.48, 30,
      0.65 , 0.7 , 0.32 , 0.16, 30,
      0.85 , 0.9 , 0.64 , 0.48, 30,
      0.85 , 0.9 , 0.32 , 0.16, 30)

points_matrix <- matrix(x_y_points, byrow = TRUE, ncol = 5)


Loop through the table, and pass the values to the plotting function.


x_points<-c()
y_points<-c()

for(row in 1:nrow(points_matrix)) {
    x_beg<-points_matrix[row, 1]
    x_end<-points_matrix[row, 2]
    y_beg<-points_matrix[row, 3]
    y_end<-points_matrix[row, 4]
    y_axis_repeats<-points_matrix[row, 5]
    x_axis_repeats<-as.integer(abs(x_end-x_beg)*DEPTH_OF_DOTS)
  
    x_points_tmp<-pop_x_array(x_beg,x_end,x_axis_repeats, y_axis_repeats)
    y_points_tmp<-pop_y_array(y_beg,y_end,x_axis_repeats, y_axis_repeats)
    
   
    x_points<-c(x_points,x_points_tmp)
    y_points<-c(y_points,y_points_tmp)
}


plot(x_points,y_points,pch='.')


Shear



Shearing is when axis is fixed and the other moves.
Either

\[\begin{bmatrix} 1&0\\ \lambda&1\\ \end{bmatrix}\]

or


\[\begin{bmatrix} 1&\lambda\\ 0&1\\ \end{bmatrix}\]

x11(width = 10,  height = 5, title="Shearing", display = "", bg="bisque")

for (t in seq(0.5,-0.5,by=-.1)) {

s<- matrix(c(1, t, 0, 1), byrow = TRUE, ncol = 2)

plot_transformation(s,x_points,y_points, "Shear")

}
for (t in seq(0.5,-0.5,by=-.1)) {

s<- matrix(c(1, 0, t, 1), byrow = TRUE, ncol = 2)

plot_transformation(s,x_points,y_points, "Shear")

}

Scaling



Scaling is when increase x or y but the direction stays the same.


Either


\[\begin{bmatrix} 1&0\\ 0&\lambda\\ \end{bmatrix}\]

or


\[\begin{bmatrix} \lambda&0\\ 0&1\\ \end{bmatrix}\]

x11(width = 10,  height = 5, title="Scaling", display = "", bg="light blue")


for (t in seq(1,1.5,by=.1)) {

s<- matrix(c(t, 0, 0, 1), byrow = TRUE, ncol = 2)
plot_transformation(s,x_points,y_points, "Scaling - Dilation")

}
for (t in seq(1.5, .5, by=-.1)) {

s<- matrix(c(t, 0, 0, 1), byrow = TRUE, ncol = 2)
plot_transformation(s,x_points,y_points, "Scaling - Contraction")

}

Rotating



The trig paradigm is that we apply the following:


\[\begin{bmatrix} cos(\theta)&-sin(\theta)\\ sin(\theta)&cos(\theta)\\ \end{bmatrix}\]


This means as our a-d values approach 1 the b-c values approach zero and vica versa


The function requires radians. Our theta is in degrees to make it easier to understand.


x11(width = 10,  height = 5, title="Rotation", display = "", bg="green")

for (theta in seq(0,360,by=10)) {
  plot_rotated(theta,x_points,y_points)
}

Projection



The next 2 flatten \(R^2\) to \(R^1\), then the next 2 produce reflections.

\(\begin{bmatrix} 0&0\\ 0&1\\ \end{bmatrix}\) and \(\begin{bmatrix} 1&0\\ 0&0\\ \end{bmatrix}\)

\(\begin{bmatrix} 1&0\\ 0&-1\\ \end{bmatrix}\) and \(\begin{bmatrix} -1&0\\ 0&1\\ \end{bmatrix}\)


x11(width = 10,  height = 5, title="Projection/Reflection", display = "", bg="bisque")


s<- matrix(c(0, 0, 0, 1), byrow = TRUE, ncol = 2)
plot_transformation(s,x_points,y_points, "Projection")
s<- matrix(c(1, 0, 0, 0), byrow = TRUE, ncol = 2)
plot_transformation(s,x_points,y_points, "Projection")
s<- matrix(c(1, 0, 0, -1), byrow = TRUE, ncol = 2)
plot_transformation(s,x_points,y_points, "Projection")
s<- matrix(c(-1, 0, 0, 1), byrow = TRUE, ncol = 2)
plot_transformation(s,x_points,y_points, "Projection")
saveGIF(ani.replay(), img.name = "tryme", convert='magick', clean=TRUE)
## [1] TRUE