Linear Transformations

Adam Rumpf, 6/3/2020

Introduction

This is an interactive demonstration of how a matrix defined by two column vectors acts as a linear transformation of the entire 2D plane. A portion of the plane is displayed, and the user can click and drag to choose the positions of two vectors. These vectors are then treated as the columns of a 2×2 matrix A, or equivalently as the images of the standard unit vectors (OverHat[i])=[({{1}, {0}})] and (OverHat[j])=[({{0}, {1}})] under A. For technical reasons the vectors are snapped in incrments of 0.25 units, making it easier to try pairs of collinear vectors to see what happens.

Moving these two vectors around defines different linear transformations, represented by drawing the transformed version of the unit lattice as it is updated in real-time. The unit parallelogram, which is the transformed version of the square [0,1]×[0,1], is also shaded, and can optionally have a sample image displayed inside it to see how the matrix transforms the image. The image is the letter “F” in order to make rotations and reflections more obvious, and it is recolored from purple to orange whenever it has been reflected.

This entire demonstration is contained within a single Manipulate command. It should be lightweight enough to simply execute the entire notebook (including the initialization code) and then start playing with it.

Code

Initialization

(* parameters and preprocessing *)
dx=0.5;(* discrete spacing *)
lim=4;(* extents of display window *)
gridlim=18lim;(* number of grid lines to draw on each side of the unit vector *)
eff={{0,0},{0,1.75},{1.5,1.75},{1.5,1.5},{0.25,1.5},{0.25,1},{1.25,1},{1.25,0.75},{0.25,0.75},{0.25,0}};(* coordinates to define a shape to place in the unit cell *)
eff=Table[eff[[i]]+{0.25,0.125},{i,1,Length[eff]}];
eff*=0.5;
ogrid=Graphics[Join[{LightGray},Table[InfiniteLine[{{x,0},{x,1}}],{x,Floor[-lim],Ceiling[lim]}],Table[InfiniteLine[{{0,y},{1,y}}],{y,Floor[-lim],Ceiling[lim]}]]];(* underlying grid *)
corners={{lim,lim},{-lim,lim},{-lim,-lim},{lim,-lim}};(* corners of display window *)
textpos=0.75;(* fraction of way from screen edge to origin for drawing text *)

Demonstration

Manipulate[Module[{mat,iarrow,jarrow,ilines,jlines,motif,geffpoints={},geff,reflection,shade,readout,corner,dists,dettext},
(* matrix defined by transformed unit vectors *)
mat=Transpose[{ivec,jvec}];
reflection=Sign[Det[mat]];
(* find corner farthest from unit vectors *)
dists=Table[EuclideanDistance[corners[[i]],ivec]+EuclideanDistance[corners[[i]],jvec],{i,1,Length[corners]}];
corner=textpos corners[[Position[dists,Max[dists]][[1,1]]]];
(* draw text *)
dettext=If[showdet==True,Graphics[{Black,Text[Style["det(A) = "<>ToString[NumberForm[Det[mat],2]],Medium],corner]}],Graphics[{}]];
(* draw motif *)
If[showmotif==True,
geffpoints=Table[mat.eff[[i]],{i,1,Length[eff]}];
geff=Graphics[{If[reflection≥0,Purple,Orange],Polygon[geffpoints]}],
geff=Graphics[{}];
];
(* shaded unit cell *)
shade=Graphics[{Yellow,Opacity[0.25],Polygon[{{0,0},ivec,ivec+jvec,jvec}]}];
(* transformed unit vectors *)
iarrow=Graphics[{Blue,Thick,Arrow[{{0,0},ivec}]}];
jarrow=Graphics[{Red,Thick,Arrow[{{0,0},jvec}]}];
(* transformed grid lines *)
ilines=Graphics[Join[{Gray},Table[InfiniteLine[{{0,0}+k jvec,ivec+k jvec}],{k,-gridlim,gridlim}]]];
jlines=Graphics[Join[{Gray},Table[InfiniteLine[{{0,0}+k ivec,jvec+k ivec}],{k,-gridlim,gridlim}]]];
Show[ogrid,shade,geff,ilines,jlines,iarrow,jarrow,dettext,PlotRange->{{-lim,lim},{-lim,lim}},Axes->True,AspectRatio->1]
],{{ivec,{1,0}},{-lim,-lim},{lim,lim},dx,Locator},{{jvec,{0,1}},{-lim,-lim},{lim,lim},dx,Locator},{{showmotif,True,"show sample image"},{False,True},Checkbox},{{showdet,True,"show determinant"},{False,True},Checkbox}]