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.
DEPTH_OF_DOTS<-50 # number of plot points within a unit
SLEEP_SECONDS<-0 # how long to sleep between framesdev.control('enable')
myani=ani.record(reset = TRUE, replay.cur = FALSE)
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")
}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='.')
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 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")
}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)
}
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