{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Chapter 5 Computing transformations with matrices"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5.1 Representing linear transformations with matrices"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5.1.1 Writing vectors and linear transformations as matrices"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "B = (\n",
    "    (0,2,1),\n",
    "    (0,1,0),\n",
    "    (1,0,-1)\n",
    ")\n",
    "\n",
    "v = (3,-2,5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[(0, 0, 1), (2, 1, 0), (1, 0, -1)]"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "list(zip(*B))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Note:** this linear combination function is from the exercise in section 4.2.5"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "from vectors import *\n",
    "def linear_combination(scalars,*vectors):\n",
    "    scaled = [scale(s,v) for s,v in zip(scalars,vectors)]\n",
    "    return add(*scaled)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "def multiply_matrix_vector(matrix, vector):\n",
    "    return linear_combination(vector, *zip(*matrix))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(1, -2, -2)"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "multiply_matrix_vector(B,v)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5.1.3 Composing linear transformations by matrix multiplication"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5.1.4 Implementing matrix multiplication"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "from vectors import *\n",
    "\n",
    "def matrix_multiply(a,b):\n",
    "    return tuple(\n",
    "        tuple(dot(row,col) for col in zip(*b))\n",
    "        for row in a\n",
    "    )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = ((1,1,0),(1,0,1),(1,-1,1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "b = ((0,2,1),(0,1,0),(1,0,-1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "((0, 3, 1), (1, 2, 0), (1, 1, 0))"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "matrix_multiply(a,b)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "c = ((1,2),(3,4))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "d = ((0,-1),(1,0))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "((2, -1), (4, -3))"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "matrix_multiply(c,d)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5.1.5 3D animation with matrix transformations"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "from math import sin,cos\n",
    "def get_rotation_matrix(t): #1\n",
    "    seconds = t/1000 #2\n",
    "    return (\n",
    "        (cos(seconds),0,-sin(seconds)),\n",
    "        (0,1,0),\n",
    "        (sin(seconds),0,cos(seconds))\n",
    "    )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "pygame 1.9.4\n",
      "Hello from the pygame community. https://www.pygame.org/contribute.html\n"
     ]
    }
   ],
   "source": [
    "!python animate_teapot.py"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5.1.6 Exercises"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Exercise:** Write a function `infer_matrix(n, transformation)` that takes a dimension (like 2 or 3) and a function that is a vector transformation assumed to be linear. It should return an $n$-by-$n$ square matrix (an $n$-tuple of $n$-tuples of numbers, which is the matrix representing the linear transformation. Of course, the output will only be meaningful if the input transformation is linear. Otherwise, it is meaningless!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "def infer_matrix(n, transformation):\n",
    "    def standard_basis_vector(i):\n",
    "        return tuple(1 if i==j else 0 for j in range(1,n+1)) #1\n",
    "    standard_basis = [standard_basis_vector(i) for i in range(1,n+1)] #2\n",
    "    cols = [transformation(v) for v in standard_basis] #3\n",
    "    return tuple(zip(*cols)) #4"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "from transforms import rotate_z_by"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "from math import pi"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "((6.123233995736766e-17, -1.0, 0.0),\n",
       " (1.0, 1.2246467991473532e-16, 0.0),\n",
       " (0, 0, 1))"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "infer_matrix(3,rotate_z_by(pi/2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Mini-project:** Write a random_matrix function that generates matrices of a specified size with random whole number entries. Use the function to generate five pairs of 3-by-3 matrices. Multiply each of the pairs together by hand (for practice), and then check your work with the matrix_multiply function."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "from random import randint\n",
    "def random_matrix(rows,cols,min=-2,max=2):\n",
    "    return tuple(\n",
    "        tuple(\n",
    "        randint(min,max) for j in range(0,cols))\n",
    "        for i in range(0,rows)\n",
    "    )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "((10, 9, 1), (1, 9, 10), (9, 3, 0))"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "random_matrix(3,3,0,10)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Exercise:** Apply the matrix `((2,1,1), (1,2,1), (1,1,2))` to all the vectors defining the teapot. What happens to the teapot and why?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "pygame 1.9.4\n",
      "Hello from the pygame community. https://www.pygame.org/contribute.html\n"
     ]
    }
   ],
   "source": [
    "!python matrix_transform_teapot.py"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Exercise:** Implement `multiply_matrix_vector` in a different way by using two nested comprehensions: one traversing the rows of the matrix and one traversing the entries of each row."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [],
   "source": [
    "def multiply_matrix_vector(matrix,vector):\n",
    "    return tuple(\n",
    "        sum(vector_entry * matrix_entry\n",
    "            for vector_entry, matrix_entry in zip(row,vector))\n",
    "        for row in matrix\n",
    "    )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(1, -2, -2)"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "multiply_matrix_vector(b,v) #should be same as before"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Exercise:** Implement `multiply_matrix_vector` yet another way using the fact that the output coordinates are the dot products of the input matrix rows with the input vector."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [],
   "source": [
    "def multiply_matrix_vector(matrix,vector):\n",
    "    return tuple(\n",
    "        dot(row,vector)\n",
    "        for row in matrix\n",
    "    )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(1, -2, -2)"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "multiply_matrix_vector(b,v) #should be same as before"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Exercise:** Once again let’s use the two matrices $$A = \\left(\\begin{array}{ccc}1&1&0\\\\1&0&1\\\\1&-1&1\\end{array}\\right) \\quad \\text{and} \\quad B = \\left(\\begin{array}{ccc}0&2&1\\\\0&1&0\\\\1&0&-1\\end{array}\\right)$$\n",
    "\n",
    "from section 5.1.3.  Write a function `compose_a_b` that executes the composition of the linear transformation for $A$ and the linear transformation for $B$. Then use the `infer_matrix` function from a previous exercise in this section to show that `infer_matrix(3, compose_a_b)` is the same as the matrix product $AB$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [],
   "source": [
    "from transforms import compose\n",
    "a = ((1,1,0),(1,0,1),(1,-1,1))\n",
    "b = ((0,2,1),(0,1,0),(1,0,-1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [],
   "source": [
    "def transform_a(v):\n",
    "    return multiply_matrix_vector(a,v)\n",
    "\n",
    "def transform_b(v):\n",
    "    return multiply_matrix_vector(b,v)\n",
    "\n",
    "compose_a_b = compose(transform_a, transform_b)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "((0, 3, 1), (1, 2, 0), (1, 1, 0))"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "infer_matrix(3,compose_a_b)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "((0, 3, 1), (1, 2, 0), (1, 1, 0))"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "matrix_multiply(a,b) #same result!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Exercise:** We can multiply a square matrix by itself any number of times. We can then think of successive matrix multiplications as “raising a matrix to a power.” For a square matrix $A$, $A \\cdot A$ can be written $A^2$; $A \\cdot A \\cdot A$ can be written $A^3$; and so on. Write a `matrix_power(power,matrix)` function that raises a matrix to the specified (whole number) power."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [],
   "source": [
    "def matrix_power(power,matrix):\n",
    "    result = matrix\n",
    "    for _ in range(1,power):\n",
    "        result = matrix_multiply(result,matrix)\n",
    "    return result"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "for example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "((-1, 4, 2), (0, 1, 0), (2, 0, -3))"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "matrix_multiply(b,matrix_multiply(b,b)) # B * B * B"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "((-1, 4, 2), (0, 1, 0), (2, 0, -3))"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "matrix_power(3,b) #same result!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5.2 Interpreting matrices of different shapes "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = ((-1, 0, -1, -2, -2), (0, 0, 2, -2, 1), (-2, -1, -2, 0, 1), (0, 2, -2, -1, 0), (1, 1, -1, -1, 0))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [],
   "source": [
    "b = ((-1, 0, -1, -2, -2), (0, 0, 2, -2, 1), (-2, -1, -2, 0, 1), (0, 2, -2, -1, 0), (1, 1, -1, -1, 0))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "((1, -5, 9, 6, 1),\n",
       " (-3, -5, -1, 1, 2),\n",
       " (7, 3, 3, 5, 1),\n",
       " (4, 0, 10, -3, 0),\n",
       " (1, -1, 5, -3, -2))"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "matrix_multiply(a,b)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5.2.1 Column vectors as matrices"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [],
   "source": [
    "c = ((-1, -1, 0), (-2, 1, 2), (1, 0, -1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [],
   "source": [
    "d = ((1,),(1,),(1,))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "((-2,), (1,), (0,))"
      ]
     },
     "execution_count": 38,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "matrix_multiply(c,d)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(-2, 1, 0)"
      ]
     },
     "execution_count": 39,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "multiply_matrix_vector(c,(1,1,1))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5.2.2 What pairs of matrices can be multiplied?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5.2.3 Viewing square and non-square matrices as vector functions"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5.2.4 Projection as a linear map from 3D to 2D"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5.2.5 Composing linear maps"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5.2.6 Exercises"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Exercise:** Write a function that turns a column vector into a row vector, or vice versa. Flipping a matrix on its side like this is called `transposition` and the resulting matrix is called the `transpose` of the original."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [],
   "source": [
    "def transpose(matrix):\n",
    "    return tuple(zip(*matrix))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "((1, 2, 3),)"
      ]
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "transpose(((1,),(2,),(3,)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "((1,), (2,), (3,))"
      ]
     },
     "execution_count": 42,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "transpose(((1, 2, 3),))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Exercise:** Show by example that the `infer_matrix` function from a previous exercise can create matrices for linear functions whose inputs and outputs have different dimensions."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [],
   "source": [
    "def project_xy(v):\n",
    "    x,y,z = v\n",
    "    return (x,y)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "((1, 0, 0), (0, 1, 0))"
      ]
     },
     "execution_count": 44,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "infer_matrix(3,project_xy)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5.3.1 Making plane translations linear"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<Figure size 600x507.692 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from vector_drawing import *\n",
    "\n",
    "dino_vectors = [(6,4), (3,1), (1,2), (-1,5), (-2,5), (-3,4), (-4,4),\n",
    "    (-5,3), (-5,2), (-2,2), (-5,1), (-4,0), (-2,1), (-1,0), (0,-3),\n",
    "    (-1,-4), (1,-4), (2,-3), (1,-2), (3,-1), (5,1)\n",
    "]\n",
    "\n",
    "draw(\n",
    "    Points(*dino_vectors),\n",
    "    Polygon(*dino_vectors)\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 48576 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "from draw3d import draw3d, Points3D, Segment3D\n",
    "\n",
    "def polygon_segments_3d(points,color='blue'):\n",
    "    count = len(points)\n",
    "    return [Segment3D(points[i], points[(i+1) % count],color=color) for i in range(0,count)]\n",
    "\n",
    "dino_3d = [(x,y,1) for x,y in dino_vectors]\n",
    "\n",
    "draw3d(\n",
    "    Points3D(*dino_3d, color='blue'),\n",
    "    *polygon_segments_3d(dino_3d),\n",
    "    save_as='figures/5.25.svg'\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [],
   "source": [
    "magic_matrix = (\n",
    "    (1,0,3),\n",
    "    (0,1,1),\n",
    "    (0,0,1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [],
   "source": [
    "translated = [multiply_matrix_vector(magic_matrix, v) for v in dino_3d]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 55360 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "draw3d(\n",
    "    Points3D(*dino_3d, color='C0'),\n",
    "    *polygon_segments_3d(dino_3d,color='C0'),\n",
    "    Points3D(*translated,color='C3'),\n",
    "    *polygon_segments_3d(translated,color='C3'),\n",
    "    save_as='figures/5.27.svg'\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {},
   "outputs": [],
   "source": [
    "translated_2d = [(x,y) for (x,y,z) in translated]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 34788 characters]\n",
      "text/plain": [
       "<Figure size 432x324 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "draw(\n",
    "    Points(*dino_vectors,color='C0'),\n",
    "    Polygon(*dino_vectors,color='C0'),\n",
    "    Points(*translated_2d, color='C3'),\n",
    "    Polygon(*translated_2d, color='C3'),\n",
    "    save_as='figures/5.28.svg'\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5.3.2 Finding a 3D matrix for a 2D translation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {},
   "outputs": [],
   "source": [
    "rotate_and_translate = ((0,-1,3),(1,0,1),(0,0,1))\n",
    "rotated_translated_dino = [\n",
    "    multiply_matrix_vector(rotate_and_translate, v) \n",
    "    for v in dino_3d]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 48756 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "draw3d(\n",
    "    Points3D(*dino_3d, color='C0'),\n",
    "    *polygon_segments_3d(dino_3d, color='C0'),\n",
    "    save_as='figures/5.32a.svg'\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 48136 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "draw3d(\n",
    "    Points3D(*rotated_translated_dino, color='C3'),\n",
    "    *polygon_segments_3d(rotated_translated_dino, color='C3'),\n",
    "    save_as='figures/5.32b.svg'\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5.3.4 Translating 3D objects in a 4D world"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {},
   "outputs": [],
   "source": [
    "def translate_3d(translation):\n",
    "    def new_function(target): #1\n",
    "        a,b,c = translation\n",
    "        x,y,z = target\n",
    "        matrix = ((1,0,0,a),(0,1,0,b),(0,0,1,c),(0,0,0,1)) #2\n",
    "        vector = (x,y,z,1)\n",
    "        x_out, y_out, z_out, _ = multiply_matrix_vector(matrix,vector) #3\n",
    "        return (x_out,y_out,z_out)\n",
    "    return new_function"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "pygame 1.9.4\n",
      "Hello from the pygame community. https://www.pygame.org/contribute.html\n"
     ]
    }
   ],
   "source": [
    "!python matrix_translate_teapot.py"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5.3.5 Exercises"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Exercise:** Show that the 3D ‘magic’ matrix transformation does not work if you move a 2D figure, such as the dinosaur we have been using, to the plane $z = 2$. What happens instead?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 42592 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 50396 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 30724 characters]\n",
      "text/plain": [
       "<Figure size 432x295.579 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Same code as above except for the 2 in this line\n",
    "dino_3d = [(x,y,2) for x,y in dino_vectors]\n",
    "######################\n",
    "\n",
    "draw3d(\n",
    "    Points3D(*dino_3d, color='blue'),\n",
    "    *polygon_segments_3d(dino_3d, color='C0')\n",
    ")\n",
    "\n",
    "translated = [multiply_matrix_vector(magic_matrix, v) for v in dino_3d]\n",
    "\n",
    "draw3d(\n",
    "    Points3D(*dino_3d, color='C0'),\n",
    "    *polygon_segments_3d(dino_3d,color='C0'),\n",
    "    Points3D(*translated,color='C3'),\n",
    "    *polygon_segments_3d(translated,color='C3')\n",
    ")\n",
    "\n",
    "translated_2d = [(x,y) for (x,y,z) in translated]\n",
    "\n",
    "draw(\n",
    "    Points(*dino_vectors, color='C0'),\n",
    "    Polygon(*dino_vectors, color='C0'),\n",
    "    Points(*translated_2d, color='C3'),\n",
    "    Polygon(*translated_2d, color='C3'),\n",
    "    save_as='figures/ex_5.26_dino.svg'\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Exercise:** Come up with a matrix to translate the dinosaur by -2 units in the $x$ direction and -2 units in the $y$ direction. Execute the transformation and show the result."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 48756 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 54792 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 37264 characters]\n",
      "text/plain": [
       "<Figure size 432x374.4 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Same code as above, except we define a new matrix\n",
    "translation_matrix = ((1,0,-2),(0,1,-2),(0,0,1))\n",
    "######################\n",
    "\n",
    "dino_3d = [(x,y,1) for x,y in dino_vectors]\n",
    "\n",
    "draw3d(\n",
    "    Points3D(*dino_3d, color='C0'),\n",
    "    *polygon_segments_3d(dino_3d, color='C0')\n",
    ")\n",
    "\n",
    "translated = [multiply_matrix_vector(translation_matrix, v) for v in dino_3d]\n",
    "\n",
    "draw3d(\n",
    "    Points3D(*dino_3d, color='C0'),\n",
    "    *polygon_segments_3d(dino_3d,color='C0'),\n",
    "    Points3D(*translated,color='C3'),\n",
    "    *polygon_segments_3d(translated,color='C3')\n",
    ")\n",
    "\n",
    "translated_2d = [(x,y) for (x,y,z) in translated]\n",
    "\n",
    "draw(\n",
    "    Points(*dino_vectors, color='C0'),\n",
    "    Polygon(*dino_vectors, color='C0'),\n",
    "    Points(*translated_2d, color='C3'),\n",
    "    Polygon(*translated_2d, color='C3'),\n",
    "    save_as='figures/ex_5.27_second_dino.svg'\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Mini-project:** Find a 3x3 matrix that rotates a 2D figure in the plane $z = 1$ by $45^\\circ$, decreases its size by a factor of 2, and translates it by the vector (2,2). Demonstrate that it works by applying it to the vertices of the dinosaur."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "((0.7071067811865476, -0.7071067811865475),\n",
       " (0.7071067811865476, 0.7071067811865476))"
      ]
     },
     "execution_count": 59,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from vectors import rotate2d\n",
    "from transforms import curry2\n",
    "from math import pi\n",
    "\n",
    "rotate_45_degrees = curry2(rotate2d)(pi/4)\n",
    "rotation_matrix = infer_matrix(2,rotate_45_degrees)\n",
    "rotation_matrix"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "((0.3535533905932738, -0.35355339059327373),\n",
       " (0.3535533905932738, 0.3535533905932738))"
      ]
     },
     "execution_count": 60,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "scale_matrix = ((0.5,0),(0,0.5))\n",
    "rotate_and_scale = matrix_multiply(scale_matrix,rotation_matrix)\n",
    "rotate_and_scale"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "((0.3535533905932738, -0.35355339059327373, 2),\n",
       " (0.3535533905932738, 0.3535533905932738, 2),\n",
       " (0, 0, 1))"
      ]
     },
     "execution_count": 61,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "((a,b),(c,d)) = rotate_and_scale\n",
    "final_matrix = ((a,b,2),(c,d,2),(0,0,1))\n",
    "final_matrix"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 62,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 42460 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 47432 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 33100 characters]\n",
      "text/plain": [
       "<Figure size 432x465.231 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Same code as above, except we use \"final matrix\"\n",
    "\n",
    "\n",
    "draw3d(\n",
    "    Points3D(*dino_3d, color='C0'),\n",
    "    *polygon_segments_3d(dino_3d, color='C0')\n",
    ")\n",
    "\n",
    "\n",
    "# use final_matrix here\n",
    "translated = [multiply_matrix_vector(final_matrix, v) for v in dino_3d]\n",
    "\n",
    "draw3d(\n",
    "    Points3D(*dino_3d, color='C0'),\n",
    "    *polygon_segments_3d(dino_3d,color='C0'),\n",
    "    Points3D(*translated,color='C3'),\n",
    "    *polygon_segments_3d(translated,color='C3')\n",
    ")\n",
    "\n",
    "translated_2d = [(x,y) for (x,y,z) in translated]\n",
    "\n",
    "draw(\n",
    "    Points(*dino_vectors, color='C0'),\n",
    "    Polygon(*dino_vectors, color='C0'),\n",
    "    Points(*translated_2d, color='C3'),\n",
    "    Polygon(*translated_2d, color='C3'),\n",
    "    save_as='figures/ex_5.3.5_third_dino.svg'\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Exercise:** Write a function analogous to `translate_3d` called `translate_4d` that uses a 5x5 matrix to translate a 4D vector by another 4D vector. Run an example to show that the coordinates are translated."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 63,
   "metadata": {},
   "outputs": [],
   "source": [
    "def translate_4d(translation):\n",
    "    def new_function(target):\n",
    "        a,b,c,d = translation\n",
    "        x,y,z,w = target\n",
    "        matrix = (\n",
    "            (1,0,0,0,a),\n",
    "            (0,1,0,0,b),\n",
    "            (0,0,1,0,c),\n",
    "            (0,0,0,1,d),\n",
    "            (0,0,0,0,1))\n",
    "        vector = (x,y,z,w,1)\n",
    "        x_out,y_out,z_out,w_out,_ = multiply_matrix_vector(matrix,vector)\n",
    "        return (x_out,y_out,z_out,w_out)\n",
    "    return new_function"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 64,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(11, 22, 33, 44)"
      ]
     },
     "execution_count": 64,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "translate_4d((1,2,3,4))((10,20,30,40))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
