An affine transformation is a geometric transformation that preserves lines and parallelism, but not necessarily Euclidean distances and angles. Examples of affine transformations include:
- translations
- shears
- rotations
- Scalings
In the app in Figure 1 you can explore transformation of a set of points.
- Are straight lines preserved for all transformations?
- For which transformations are the angles between neighbouring points preserved?
- does an inverse transformation exist (i.e. an additional transformation that maps the black square back on to the red one?)
#| standalone: true
#| components: [viewer]
#| viewerHeight: 600
from shiny import App, Inputs, Outputs, Session, render, ui
from shiny import reactive
import numpy as np
import matplotlib.pyplot as plt
import skimage as ski
import pyodide
from skimage.transform import SimilarityTransform
from skimage.transform import warp
from skimage.transform import PiecewiseAffineTransform
from skimage.transform import AffineTransform
from skimage.io import imread
import io
from pathlib import Path
from skimage.transform import resize, rescale
app_ui = ui.page_fluid(
ui.layout_sidebar(
ui.sidebar(
ui.input_slider(id="Rotation",label="Rotation",min=-2.0*np.pi,max=2.0*np.pi,value=0.0,step=np.pi/20.0),
ui.input_slider(id="Scale",label="Scale",min=0.1,max=3.0,value=1.0,step=0.1),
ui.input_slider(id="Shear",label="Shear",min=-3.0,max=3.0,value=0.0,step=0.1),
ui.input_slider(id="translate_x",label="translate_x",min=-3.0,max=3.0,value=1.0,step=0.1),
ui.input_slider(id="translate_y",label="translate_y",min=-3.0,max=3.0,value=1.0,step=0.1)),
ui.output_plot("plot"),
),
)
def server(input: Inputs, output: Outputs, session: Session):
@render.plot
def plot():
# Calculate the translation required to move all points into positive coordinates
translation_vec=[input.translate_x(),input.translate_y()]
scale_param=input.Scale()
shear_param=input.Shear()
rotation_param=input.Rotation()
tform_shift = AffineTransform(translation=translation_vec,scale=scale_param,shear=shear_param,rotation=rotation_param)
homog_trans_matrix=tform_shift.params
old_coordinates=[[0,0,1.0],[0,1,1.0],[0,2,1.0],[0,3,1.0],[0,4,1.0],
[4.0,0,1.0],[4.0,1,1.0],[4.0,2,1.0],[4.0,3,1.0],[4.0,4,1.0],
[1.0,0.0,1.0],[2.0,0.0,1.0],[3.0,0.0,1.0],
[1.0,4.0,1.0],[2.0,4.0,1.0],[3.0,4.0,1.0]]
old_coordinates=np.array(old_coordinates)
new_coordinates=old_coordinates.copy()
for i in range(old_coordinates.shape[0]):
homog_vec=np.array(old_coordinates[i,:]).copy()
new_coordinates[i,:]=homog_trans_matrix@homog_vec
fig,ax=plt.subplots()
ax.plot()
ax.set_xticks(np.arange(-10,10))
ax.set_yticks(np.arange(-10,10))
ax.plot(old_coordinates[:,0],old_coordinates[:,1],'r*')
ax.plot(new_coordinates[:,0],new_coordinates[:,1],'k*')
ax.grid(True)
plt.axis('equal')
ax.set_xlim([-10,10])
ax.set_ylim([-10,10])
ax.legend(['Original','Transformed'])
plt.show()
app = App(app_ui, server)
At Dundee, concepts from geometry are studied in core modules Maths 1A and Maths 1B.
At Level 3 in the module Differential Geometry students study generalisations of 2D mappings to arbitrarily curved spaces.
You can find out more about these modules here. :::