summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--idris/basic.idr86
-rw-r--r--idris/thing.idr214
-rw-r--r--nim/install.sh1
-rw-r--r--nim/oggie.nimble15
-rw-r--r--nim/src/oggie.nim205
-rw-r--r--nim/src/oggiepkg/submodule.nim1
-rw-r--r--python/basic.py107
-rw-r--r--report/report.tex119
-rw-r--r--report/stacs_cw.cls106
9 files changed, 854 insertions, 0 deletions
diff --git a/idris/basic.idr b/idris/basic.idr
new file mode 100644
index 0000000..0d67f1d
--- /dev/null
+++ b/idris/basic.idr
@@ -0,0 +1,86 @@
+import Data.Vect
+import System as System
+
+import Graphics.SDL2 as SDL2
+
+Point : Nat -> Type
+Point n = Vect n Double
+
+(+) : Point n -> Point n -> Point n
+xs + ys = zipWith (+) xs ys
+
+(*) : Double -> Point n -> Point n
+n * xs = map ((*) n) xs
+
+nCr : Nat -> Nat -> Double
+nCr n r = (cast $ fact n) / (cast $ fact r) / (cast $ fact $ minus n r)
+
+bernstein : Nat -> Nat -> Double -> Double
+bernstein i n t = (nCr n i) * (pow t i) * (pow (1 - t) $ minus n i)
+
+nats : (n: Nat) -> Vect n Nat
+nats (S k) = reverse $ zo k where
+ zo Z = Z::[]
+ zo (S k) = (S k)::(zo k)
+
+sump : Vect n $ Point d -> Point d
+sump (x::xs) = foldr (+) x xs
+
+bezier : Vect n $ Point d -> Double -> Point d
+bezier {n} ps t = sump $ zipWith bs ps $ nats n where
+ bs p i = (bernstein i (minus n 1) t) * p
+
+drawBetween : Renderer -> List $ Point 2 -> Int -> Int -> Int -> Int -> IO ()
+drawBetween _ [] _ _ _ _ = pure ()
+drawBetween _ [p] _ _ _ _ = pure ()
+drawBetween rend (p::q::ps) r g b a = do
+ SDL2.drawLine rend (cast $ head p) (cast $ last p)
+ (cast $ head q) (cast $ last q) r g b a
+ drawBetween rend (q::ps) r g b a
+
+drawPoints : Renderer -> Vect n $ Point 2 -> Int -> Int -> Int -> Int -> IO ()
+drawPoints _ [] _ _ _ _ = pure ()
+drawPoints rend (p::ps) r g b a = do
+ SDL2.filledEllipse rend (cast $ head p) (cast $ last p) 10 10 r g b a
+ drawPoints rend ps r g b a
+
+restOfTheOwl : Renderer -> Vect n $ Point 2 -> IO ()
+restOfTheOwl rend ps = (do
+ drawPoints rend ps 255 0 0 255
+ drawBetween rend (map (\x => bezier ps x) samples) 255 255 255 255) where
+ samples : List Double
+ samples = [(cast x) / 10 | x <- [1..10]]
+
+-- Taken from https://github.com/steshaw/idris-sdl2/blob/master/test/hello.idr
+fail : (msg: String) -> IO ()
+fail msg = do
+ err <- getError
+ fPutStr stderr $ msg ++ " failed:" ++ err
+ fflush stderr
+ System.exit 1
+
+main : IO ()
+main = (do
+ renderer <- SDL2.init 500 500
+ loop renderer [])
+ where
+ loop : Renderer -> Vect n $ Point 2 -> IO ()
+ loop renderer cps = do
+ putStrLn "frame"
+ True <- SDL2.setRendererDrawColor renderer 0 0 0 255
+ | fail "setRendererDrawColor"
+ SDL2.renderClear renderer
+ restOfTheOwl renderer cps
+ SDL2.renderPresent renderer
+ event <- SDL2.pollEvent
+ case event of
+ Just (SDL2.AppQuit) => do
+ pure ()
+ Just (SDL2.KeyDown SDL2.KeyEsc) => do
+ pure ()
+ Just (SDL2.MouseButtonDown Left a b) => do
+ putStrLn $ show a
+ putStrLn $ show b
+ loop renderer (cps ++ [[cast a, cast b]])
+ _ => do
+ loop renderer cps
diff --git a/idris/thing.idr b/idris/thing.idr
new file mode 100644
index 0000000..353523d
--- /dev/null
+++ b/idris/thing.idr
@@ -0,0 +1,214 @@
+import Data.Vect
+import Data.Fin
+import System as System
+
+import Graphics.SDL2 as SDL2
+
+infixr 2 <:>
+infixr 4 </>
+
+Point : Nat -> Type
+Point n = Vect n Double
+
+Simplex : Nat -> Type
+Simplex n = Vect (S n) $ Point (S n)
+
+(+) : Point n -> Point n -> Point n
+xs + ys = zipWith (+) xs ys
+
+(*) : Double -> Point n -> Point n
+n * xs = map ((*) n) xs
+
+average : Double -> Double -> Double
+average a b = (a + b) / 2
+
+midpoint : Point n -> Point n -> Point n
+midpoint a b = zipWith average a b
+
+nCr : Nat -> Nat -> Double
+nCr n r = (cast $ fact n) / (cast $ fact r) / (cast $ fact $ minus n r)
+
+bernstein : Nat -> Nat -> Double -> Double
+bernstein i n t = (nCr n i) * (pow t i) * (pow (1 - t) $ minus n i)
+
+nats : (n: Nat) -> Vect n Nat
+nats (S k) = reverse $ zo k where
+ zo Z = Z::[]
+ zo (S k) = (S k)::(zo k)
+
+RVect : Vect m Nat -> Type -> Type
+RVect [] t = t
+RVect (n::ns) t = Vect n (RVect ns t)
+
+Matrix : Nat -> Nat -> Type -> Type
+Matrix n m t = Vect m $ Vect n t
+
+RTuple : Vect n Type -> Type
+RTuple [] = ()
+RTuple [t] = t
+RTuple (t::ts) = (t, RTuple ts)
+
+BezierFunc : Nat -> Nat -> Type
+BezierFunc Z d = Point d
+BezierFunc (S n) d = (Double -> BezierFunc n d)
+
+-- bezier : {ds: Vect e Nat} -> RVect ds $ Point d -> BezierFunc e d
+
+(<:>) : Num a => Vect n a -> Vect n a -> a
+w <:> v = sum $ zipWith (*) w v
+
+(</>) : Num a => Matrix n m a -> Vect n a -> Vect m a
+m </> v = map (v <:>) m
+
+origin : (n: Nat) -> Point n
+origin Z = []
+origin (S k) = 0::(origin k)
+
+sump : Vect n $ Point d -> Point d
+sump (x::xs) = foldr (+) x xs
+
+comb : Vect n a -> Vect m b -> Vect (n * m) (a, b)
+comb (a::as) bs = (comb' a bs) ++ comb as bs where
+ comb' a bs = map (\x => (a, x)) bs
+
+vcomb : Vect n a -> Vect m a -> Vect (n * m) $ Vect 2 a
+vcomb as bs = map (\(a,b) => [a,b]) $ comb as bs
+
+vvcomb : Vect n a -> Vect m $ Vect o a -> Vect (n * m) $ Vect (S o) a
+vvcomb (x::xs) ys = (vvcomb' x ys) ++ vvcomb xs ys where
+ vvcomb' x ys = map ((::)x) ys
+
+Indices : Vect n Nat -> Type
+Indices [] = ()
+Indices (n::ns) = (Fin n, Indices ns)
+
+-- Create a combination of all numbers counting up to the number mentioned in
+-- the input list.
+-- For example, [4, 2] should produce
+-- `[[0,0],[0,1],[1,0],[1,1],[2,0],[2,1],[3,0],[3,1]]`
+natComb : (ds: Vect n Nat) -> Vect (product ds) $ Indices ds
+
+bezier : Vect n $ Point d -> Vect 1 Double -> BezierFunc 0 d
+bezier {n} ps [t] = sump $ zipWith bs ps $ nats n where
+ bs p i = (bernstein i (minus n 1) t) * p
+
+finer : Vect (S (S n)) Double -> Vect (S (2 * (S n))) Double
+
+index : {ds: Vect n Nat} -> Indices ds -> RVect ds a -> a
+index {ds = []} _ x = x
+index {ds = _::[]} (i, ()) rv = index i rv
+index {ds = _::_} is rv = Main.index (snd is) $ index (fst is) rv
+
+asVect : {ds: Vect n Nat} -> Indices ds -> Vect n Nat
+asVect {ds = []} _ = []
+asVect {ds = _::[]} (i, ()) = [finToNat i]
+asVect {ds = _::_} is = (finToNat $ Basics.fst is)::(asVect $ Basics.snd is)
+
+bezier' : {ds: Vect e Nat} -> RVect ds $ Point d -> Vect e Double -> BezierFunc 0 d
+bezier' {ds} x ts = sump $ map (\i => (bernsteins i ds ts) * (Main.index i x)) (natComb ds) where
+ bernsteins i ds ts = product $ zipWith3 bernstein (asVect i) ds ts
+
+bezier'' : {ds: Vect e Nat} -> RVect ds $ Point d -> BezierFunc e d
+
+-- Taken from https://github.com/steshaw/idris-sdl2/blob/master/test/hello.idr
+fail : (msg: String) -> IO ()
+fail msg = do
+ err <- getError
+ fPutStr stderr $ msg ++ " failed:" ++ err
+ fflush stderr
+ System.exit 1
+
+test : Vect 4 $ Point 2
+test = [[179,381],[100,100],[447,342],[353,92]]
+
+divNat : Double -> Double -> Nat
+divNat a b =
+ if a < 0 then Z else S (divNat (a - b) b)
+
+-- between : (l: Double) -> (u: Double) -> (s: Double) -> Vect ((divNat (u - l) s) + 0) Double
+-- between lower upper step =
+-- if lower < upper then lower::(between (lower + step) upper step) else [upper]
+
+model : List $ Point 2
+model = map (\t => bezier test [t]) [(cast x) / 10 | x <- [1..10]]
+
+samplePoints : Vect n Double -> Matrix x y $ Vect 2 Double -> Matrix x y $ Vect (n + 2) Double
+samplePoints ts xy = map (\cs => map (\c => ts ++ c) cs) xy
+
+getSamples : {ds: Vect e Nat} -> RVect ds $ Point d -> Matrix x y $ Vect e Double -> Matrix x y $ Point d
+-- getSamples rv ts xy = map (\cs => map (\c => bezier' rv (ts ++ c)) cs) xy
+getSamples rv tss = map doRows tss where
+ doCols : Vect e Double -> Point d
+ doCols ts = bezier' rv ts
+ doRows : Vect x $ Vect e Double -> Vect x $ Point d
+ doRows cs = map doCols cs
+
+xrotator : Double -> Matrix 3 3 Double
+xrotator a = [[1,0,0],[0, cos a, -sin a],[0, sin a, cos a]]
+
+yrotator : Double -> Matrix 3 3 Double
+yrotator a = [[cos a, 0, sin a],[0,1,0],[-sin a, 0, cos a]]
+
+reduceTo3d : Point n -> Point 3
+reduceTo3d {n = Z} p = reduceTo3d [0]
+reduceTo3d {n = (S Z)} [x] = reduceTo3d [x, 0]
+reduceTo3d {n = (S (S Z))} [x, y] = [x, y, 0]
+reduceTo3d {n = (S (S (S _)))} p = take 3 p
+
+reduceTo2d : Point n -> Vect 2 Int
+reduceTo2d {n = Z} p = reduceTo2d [0]
+reduceTo2d {n = (S Z)} [x] = reduceTo2d [x, 0]
+reduceTo2d {n = (S (S _))} p = map cast $ take 2 p
+
+-- drawModel : Model -> (Double, Double) -> IO ()
+-- drawModel m (x,y) = do
+-- three <-
+
+drawBetween : Renderer -> List $ Point 2 -> Int -> Int -> Int -> Int -> IO ()
+drawBetween _ [] _ _ _ _ = pure ()
+drawBetween _ [p] _ _ _ _ = pure ()
+drawBetween rend (p::q::ps) r g b a = do
+ SDL2.drawLine rend (cast $ head p) (cast $ last p)
+ (cast $ head q) (cast $ last q) r g b a
+ drawBetween rend (q::ps) r g b a
+
+drawPoints : Renderer -> Vect n $ Point 2 -> Int -> Int -> Int -> Int -> IO ()
+drawPoints _ [] _ _ _ _ = pure ()
+drawPoints rend (p::ps) r g b a = do
+ SDL2.filledEllipse rend (cast $ head p) (cast $ last p) 10 10 r g b a
+ drawPoints rend ps r g b a
+
+main : IO ()
+main = (do
+ renderer <- SDL2.init 500 500
+ loop renderer
+ SDL2.destroyRenderer renderer
+ quit)
+ where
+ loop : Renderer -> IO ()
+ loop renderer = do
+ False <- SDL2.pollEventsForQuit | pure ()
+ True <- SDL2.setRendererDrawColor renderer 0 0 0 255
+ | fail "setRendererDrawColor"
+ SDL2.renderClear renderer
+ drawPoints renderer test 255 0 0 255
+ drawBetween renderer model 255 255 255 255
+ SDL2.renderPresent renderer
+ loop renderer
+
+-- bezier : Vect m $ Vect (S 1) Double -> Vect 1 Double -> Vect (S 1) Double
+-- bezier [ p0 ] [ t ] = p0
+-- bezier ps [ t ] = sum $ zipWith bernstein' ps $ oneOnwards l where
+
+-- bezier ps [t] = sum (map (bernstein t l) ps)
+-- bezier xs (x :: ys) = ?bezier_rhs_2 where
+-- bezier' ps t = sum (map (bernstein t l) ps)
+
+-- bezier [p0] t = p0
+-- bezier ps@(_::_) t = (1 - t)*(bezier (init ps) t) + (t)*(bezier (tail ps) t)
+
+-- test : Vect 4 (Vect 2 Double)
+-- test = [ [ 100.0, 250.0], [ 100.0, 100.0 ], [ 400.0, 400.0 ], [ 400.0, 250.0 ] ]
+
+-- testOut : Vect 2 Double
+-- testOut = bezier test 0.5
diff --git a/nim/install.sh b/nim/install.sh
new file mode 100644
index 0000000..6012653
--- /dev/null
+++ b/nim/install.sh
@@ -0,0 +1 @@
+curl https://nim-lang.org/choosenim/init.sh -sSf | sh
diff --git a/nim/oggie.nimble b/nim/oggie.nimble
new file mode 100644
index 0000000..7f0eb3c
--- /dev/null
+++ b/nim/oggie.nimble
@@ -0,0 +1,15 @@
+# Package
+
+version = "0.1.0"
+author = "Tom Harley"
+description = "Graphics Stuff"
+license = "MIT"
+srcDir = "src"
+installExt = @["nim"]
+bin = @["oggie"]
+
+
+# Dependencies
+
+requires "nim >= 0.19.0"
+requires "nimsvg"
diff --git a/nim/src/oggie.nim b/nim/src/oggie.nim
new file mode 100644
index 0000000..c7346a9
--- /dev/null
+++ b/nim/src/oggie.nim
@@ -0,0 +1,205 @@
+import strformat
+import sequtils
+import tables
+import random
+import math
+import sugar
+
+import nimsvg
+
+type point = tuple[ x, y: float ]
+type vec = tuple[ x, y: float ]
+type line = tuple[ a, b: point ]
+type colour = string
+
+var draw_line: seq[(line, colour)] = @[]
+var draw_point: seq[(point, colour)] = @[]
+
+proc get_random_points(n = 10, bounds = (x: 100.0, y: 100.0)): seq[point] =
+ result = new_seq[point](n)
+ for i in 0..<n:
+ result[i] = (rand(bounds.x), rand(bounds.y))
+
+iterator tail[A](s: seq[A]): A =
+ var first = true
+ for x in s:
+ if not first:
+ yield x
+ else:
+ first = false
+
+proc rightmost(points: seq[point]): point =
+ result = points[0]
+ for p in points.tail():
+ if p.x > result.x:
+ result = p
+
+proc leftmost(points: seq[point]): point =
+ result = points[0]
+ for p in points.tail():
+ if p.x < result.x:
+ result = p
+
+proc vec_to(a, b: point): vec = (b.x - a.x, b.y - a.y)
+
+proc mag(v: vec): float = hypot(v.x, v.y)
+
+proc dot(a, b: vec): float = a.x * b.x + a.y * b.y
+
+proc `ยท`(a, b: vec): float = dot(a, b)
+
+proc angle_to(a, b: vec): float = arccos((a.dot(b)) / (a.mag() * b.mag()))
+
+const up: vec = (0.0, 100.0)
+
+proc displaced(p: point, v: vec): point = (p.x + v.x, p.y + v.y)
+
+proc line_from(v: vec, p: point): line = (p, p.displaced(v))
+
+proc find_lefter(b, p, q: point): point =
+ let
+ bp = b.vec_to(p)
+ bq = b.vec_to(q)
+ if up.angle_to(bp) < up.angle_to(bq):
+ return p
+ else:
+ return q
+
+proc find_lefter(e, b, p, q: point): point =
+ let
+ eb = e.vec_to(b)
+ bp = b.vec_to(p)
+ bq = b.vec_to(q)
+ if eb.angle_to(bp) < eb.angle_to(bq):
+ return p
+ else:
+ return q
+
+proc `==`(a, b: point): bool =
+ return a.x == b.x and a.y == b.y
+
+iterator all_but_last[A](s: seq[A]): A =
+ for i in 0..<(s.len() - 1):
+ yield s[i]
+
+proc gift_wrapping(S: seq[point]): seq[point] = # O(nh)
+ var i = 0
+ var point_on_hull = leftmost(S)
+ var hull = init_table[int, point]()
+ while true:
+ echo i
+ hull[i] = point_on_hull
+ var endpoint = S[0]
+ for p in S.tail():
+ if p == point_on_hull:
+ continue
+ else:
+ if i == 0:
+ endpoint = find_lefter(hull[i], endpoint, p)
+ else:
+ endpoint = find_lefter(hull[i-1], hull[i], endpoint, p)
+ i = i + 1
+ point_on_hull = endpoint
+ if endpoint == hull[0]:
+ break
+ result = new_seq[point](i)
+ for j in 0..<i:
+ result[j] = hull[j]
+
+proc distance_to(a, b: point): float = a.vec_to(b).mag()
+
+proc times(x: (float, float), m: float): (float, float) = (x[0] * m, x[1] * m)
+
+proc `+`(a: point, b: (float, float)): point = (a.x + b[0], a.y + b[1])
+
+proc grad(l: line): float =
+ let dv = l.a.vec_to(l.b)
+ result = dv.y / dv.x
+
+proc perpendicular(l: line, c: point): line =
+ let
+ grad = l.grad()
+ perp = - (1 / grad)
+ newp = c + ((1.0, perp).times(100.0))
+ result = (c, newp)
+
+proc perpendicular(l: line): line = l.perpendicular(l.a)
+
+proc find_horizontal_intersection(l: line, y = 0.0): point =
+ let grad = l.grad()
+ return ((l.a.x - ((l.a.y - y) / grad)), y)
+
+proc find_vertical_intersection(l: line, x = 0.0): point =
+ let grad = l.grad()
+ return (x, l.a.y - (grad * (l.a.x - x)))
+
+proc distance(a, b: point): float =
+ sqrt((b.x - a.x) * (b.x - a.x) + (b.y - a.y) * (b.y - a.y))
+
+proc meets(a, b: line): point =
+ let
+ ma = a.grad()
+ mb = b.grad()
+ ca = a.find_vertical_intersection().y
+ cb = b.find_vertical_intersection().y
+ y = (mb * ca - ma * cb) / (mb - ma)
+ return a.find_horizontal_intersection(y)
+
+proc project(l: line, p: point, o: point): point =
+ let
+ drop_line = l.perpendicular(p)
+ drop_point = l.meets(drop_line)
+ x = o.distance(drop_point)
+ y = drop_point.distance(p)
+ return (x, y)
+
+proc project(l: line, p: point): point =
+ project(l, p, l.find_horizontal_intersection(0.0))
+
+proc project(l: line, ps: seq[point]): seq[point] =
+ ps.map((p) => l.project(p))
+
+proc divide(l: line, ps: seq[point]): (seq[point], seq[point]) =
+ result = (@[], @[])
+ for p in ps:
+ echo p.y, " -> ", l.project(p).y
+ if l.project(p).y < 0:
+ result[0].add(p)
+ else:
+ result[1].add(p)
+
+proc quickhull(S: seq[point]): seq[point] = # O(nlogn)
+ assert(S.len() >= 2)
+ var hull = new_seq[point]()
+ let
+ l = leftmost(S)
+ r = rightmost(S)
+
+proc line_to(a, b: point): line =
+ return (a, b)
+
+proc main() =
+ let ps = get_random_points(10)
+ for p in ps:
+ draw_point.add((p, "black"))
+ let ch = gift_wrapping(ps)
+ for i in 1..<ch.len():
+ draw_line.add((ch[i-1].line_to(ch[i]), "red"))
+
+proc draw() =
+ build_svg_file("out.svg"):
+ svg(width=100, height=100):
+ rect(x = 0, y = 0, width = 100, height = 100, fill = "#FFF")
+ for d in draw_point:
+ let (p, c) = d
+ circle(cx = p.x, cy = p.y, r = 1.5, fill = c)
+ for d in draw_line:
+ let
+ (p, c) = d
+ (a, b) = p
+ line(x1 = a.x, y1 = a.y, x2 = b.x, y2 = b.y, stroke = c,
+ `stroke-width` = 1)
+
+randomize()
+main()
+draw()
diff --git a/nim/src/oggiepkg/submodule.nim b/nim/src/oggiepkg/submodule.nim
new file mode 100644
index 0000000..7ec3b29
--- /dev/null
+++ b/nim/src/oggiepkg/submodule.nim
@@ -0,0 +1 @@
+proc getWelcomeMessage*(): string = "Hello, World!"
diff --git a/python/basic.py b/python/basic.py
new file mode 100644
index 0000000..6bb78e7
--- /dev/null
+++ b/python/basic.py
@@ -0,0 +1,107 @@
+import sys
+from functools import partial, reduce
+from math import factorial as fact
+import sdl2
+import sdl2.ext
+import ctypes
+from collections import namedtuple
+import operator as op
+
+Point = namedtuple('Point', 'x y')
+
+sdl2.ext.init()
+window = sdl2.ext.Window("basic spec", size=(500,500))
+renderer = sdl2.ext.Renderer(window)
+control_points = []
+
+def draw_points():
+ for x, y in control_points:
+ rect = sdl2.SDL_Rect(x-2, y-2, 4, 4)
+ renderer.color = sdl2.ext.Color(255,0,0,255)
+ sdl2.SDL_RenderFillRect(renderer.sdlrenderer, rect)
+
+# Taken from https://stackoverflow.com/a/4941932
+def ncr(n, r):
+ return fact(n) / (fact(r) * fact(n-r))
+
+def bernstein(i, n, t):
+ return ncr(n, i) * pow(t, i) * pow(1-t, n-i)
+
+def bern_point(i, n, t, p):
+ b = bernstein(i, n, t)
+ result = Point(p.x * b, p.y * b)
+ print(i,n,t,p,result)
+ return result
+
+def bezier(t):
+ result = Point(0,0)
+ for i, p in enumerate(control_points):
+ bp = bern_point(i, len(control_points)-1, t, p)
+ result = Point(result.x + bp.x, result.y + bp.y)
+ print(t ,result)
+ return result
+
+def bezier_it():
+ for t in range(0,101,1):
+ yield bezier(t/100)
+
+def pairs_from(it):
+ first = True
+ prev = None
+ for x in it():
+ if first:
+ first = False
+ else:
+ yield (prev, x)
+ prev = x
+
+def draw_bezier():
+ renderer.color = sdl2.ext.Color(255,255,255,255)
+ for a, b in pairs_from(bezier_it):
+ sdl2.SDL_RenderDrawLine(renderer.sdlrenderer,
+ int(a.x), int(a.y), int(b.x), int(b.y))
+
+def draw_tangent():
+ l, m, r = bezier(0.49), bezier(0.50), bezier(0.51)
+ delta = Point(r.x - l.x, r.y - l.y)
+ delta = Point(delta.x * 20, delta.y * 20)
+ renderer.color = sdl2.ext.Color(100,100,255,255)
+ sdl2.SDL_RenderDrawLine(renderer.sdlrenderer,
+ int(m.x), int(m.y),
+ int(m.x + delta.x), int(m.y + delta.y))
+ sdl2.SDL_RenderDrawLine(renderer.sdlrenderer,
+ int(m.x - delta.y), int(m.y + delta.x),
+ int(m.x + delta.y), int(m.y - delta.x))
+
+def run():
+ window.show()
+ running = True
+ while running:
+ events = sdl2.ext.get_events()
+ for event in events:
+ if event.type == sdl2.SDL_QUIT:
+ running = False
+ break
+ elif event.type == sdl2.SDL_MOUSEBUTTONDOWN:
+ x, y = ctypes.c_int(0), ctypes.c_int(0)
+ state = sdl2.mouse.SDL_GetMouseState(
+ ctypes.byref(x), ctypes.byref(y))
+ x, y = x.value, y.value
+ print(x, y)
+ control_points.append(Point(x, y))
+
+ renderer.color = sdl2.ext.Color(0,0,0,255)
+ sdl2.SDL_RenderClear(renderer.sdlrenderer)
+
+ draw_points()
+
+ if len(control_points) > 1:
+ draw_bezier()
+ draw_tangent()
+
+ sdl2.SDL_RenderPresent(renderer.sdlrenderer)
+ window.refresh()
+ return 0
+
+if __name__ == "__main__":
+ sys.exit(run())
diff --git a/report/report.tex b/report/report.tex
new file mode 100644
index 0000000..1744d89
--- /dev/null
+++ b/report/report.tex
@@ -0,0 +1,119 @@
+\documentclass{stacs_cw}
+
+\def\name{\emph}
+
+\matrno{150001937}
+
+\begin{document}
+
+\section{Introduction}
+
+B\'ezier curves and surfaces are a fundamental aspect of modern
+three-dimensional computer graphics. The ability to produce and manipulate these
+elements is crucial to forming smooth-looking surfaces in computer-generated
+imagery.
+
+For this practical, a B\'ezier curve visualiser was implemented in
+\name{Python}.
+
+As extensions, a convex hull visualiser was written in \name{Nim}, and the
+rendering of $n$-dimensional space was explored in \name{Idris}.
+
+\section{Building and Running the Submission}
+
+\subsection{Python B\'ezier Curve Visuliser}
+
+The Python code provided relies on one external library:
+
+\begin{verbatim}
+ pip3 install --user pysdl2
+\end{verbatim}
+
+After installation, the script \file{basic.py} can be run with \code{python3} on
+the lab machines.
+
+Left-clicking in the window will produce a control point. A B\'ezier curve will
+be drawn using control points chosen by the user. A tangent vector, as well as
+the vectors perpendicular to the tangent vector, will be rendered for the point
+where the parameter $t = 0.5$.
+
+\subsection{Nim Convex Hull Visualiser}
+
+A script is provided in the \file{nim} folder that will interactively install
+\code{nim} and \code{nimble} on the lab machines. After running this script, the
+command \code{nimble build} should compile the code into an executable that,
+when run, produces an SVG file with 100 randomly-placed points and their convex
+hull.
+
+\subsection{Idris $n$-dimensional B\'ezier Visualiser}
+
+The provided Idris code fails to run. The wrapper for \name{SDL} foreign
+functions causes a segmentation fault to occur when using mouse events, which is
+necessary for the implementation. The code has been included as evidence of
+effort.
+
+\section{Finding the Tangent Vector}\label{sec:tangent}
+
+The tangent vector was found by taking two sample points on the curve --- one on
+either side of the point for which the tangent vector was to be found. From
+these two points, $dx$ and $dy$ were calculated. These two values form an
+approximation of the tangent vector. The closer the sample points are to the
+original point, the more accurate this approximation will be.
+
+\section{Finding the Curvature Vector}
+
+One of the two vectors perpendicular to the tangent vector is the curvature
+vector. The submission does not discern this, and as such renders both of these
+vectors.
+
+One way to determine which of the two is the curvature vector would be to take a
+point along each of the vectors in question, and compare their distances to
+either of the sample points used in \S\ref{sec:tangent}. Except in corner cases,
+the curvature vector would be the vector with the shorter distance.
+
+Another option for finding the curvature vector is to find the second-order
+derivative of the curve, by using the $dx$/$dy$ pair mentioned earlier with
+other samples.
+
+\section{Extension: Convex Hull}
+
+The \name{Jarvis March} algorithm was implemented in Nim to find the convex hull
+of a set of points.
+
+The implementation seems to fail at times when approaching the top-right corner.
+This may be because rather than finding the ``left-most'' point (as described in
+the algorithm's
+pseudocode),
+the implementation finds the point with the smallest difference in angle.
+Floating-point arithmetic, or trigonometric inaccuracies in the functions being
+used, may cause the implementation to choose an incorrect point.
+
+\section{Extension: $n$-dimension B\'ezier... Things?}
+
+In 2D it's a B\'ezier curve, in 3D it's a B\'ezier surface, but what name does
+it gain after that point?
+
+Writing one set of functions for rendering in an arbitrary number of dimensions
+is tricky in a traditional language. In a dynamically-typed language, the number
+of dimensions elements exist in has to be constantly asserted to be equal to the
+other elements in use; in a statically-typed variant functions would have to be
+written for every $n$ dimensions. It was concluded that a statically-typed
+language with dynamic types would allow for the creation of functions that could
+handle any number of dimensions, while keeping all the assertions of degree to
+compile-time.
+
+Implementing this in Idris was significantly more difficult than had been
+originally predicted. Pile on top the fact that Idris is difficult to build on
+the lab machines, and that it lacks a package manager...
+
+I was very close to completing my goal. The final version for submission exists
+in \file{thing.idr}. This file is one function away from being able to calculate
+sample points for an arbitrary dimension count. I was so close! When I found out
+that the \name{SDL} bindings were temperamental, however, I abandoned it to
+complete the basic specification in Python.
+
+A lot of the work in \file{thing.idr} is work that noone else has ever attempted
+in Idris before --- it's quite cool to think I was working on something noone
+else has done before, even if I didn't quite finish it.
+
+\end{document}
diff --git a/report/stacs_cw.cls b/report/stacs_cw.cls
new file mode 100644
index 0000000..a5d7405
--- /dev/null
+++ b/report/stacs_cw.cls
@@ -0,0 +1,106 @@
+% Style file for producing University of St Andrew's Computer Science report - 2018
+% Work-in-Progress [Last Updated: 2018-11-05]
+
+\NeedsTeXFormat{LaTeX2e}
+\ProvidesClass{stacs_cw}[St Andrews' Computer Science coursework template]
+
+%% Load article as base class
+\LoadClass[a4paper,11pt]{article}
+%% Layout
+\RequirePackage[a4paper,headheight=14pt,width=150mm,top=25mm,bottom=25mm]{geometry}
+\RequirePackage{graphicx}
+\RequirePackage{setspace}
+\RequirePackage{float}
+%Font
+\RequirePackage{libertine}
+\RequirePackage{libertinust1math}
+\RequirePackage{inconsolata}
+%% Language and Encoding
+\RequirePackage[UKenglish]{babel}
+\RequirePackage[utf8]{inputenc}
+\RequirePackage[T1]{fontenc}
+%% Formatting
+\RequirePackage{microtype}
+\RequirePackage{xcolor}
+\RequirePackage{hyperref}
+\RequirePackage{parskip}
+%% Feature inclusion
+\RequirePackage{listings}
+\RequirePackage{fancyhdr}
+% Title
+\RequirePackage{titletoc}
+\RequirePackage{titlesec}
+%% Maths
+\RequirePackage{amsmath}
+\RequirePackage{amssymb}
+% Bibliography
+\RequirePackage{natbib}
+%% Colours
+\definecolor{st-a-gray}{gray}{0.95}
+\definecolor{st-a-blue}{HTML}{00539b}
+\definecolor{st-a-red}{HTML}{ee312a}
+\definecolor{st-a-yellow}{HTML}{ffdf00}
+\definecolor{st-a-black}{HTML}{231f20}
+\definecolor{st-a-light-blue}{HTML}{00aeef}
+\definecolor{st-a-mid-blue}{HTML}{007dc5}
+\definecolor{st-a-burgundy}{HTML}{c60c46}
+\definecolor{st-a-purple}{HTML}{7b439a}
+\definecolor{st-a-orange}{HTML}{f5842b}
+\definecolor{st-a-green}{HTML}{54b948}
+\definecolor{st-a-mid-green}{HTML}{00853f}
+\definecolor{st-a-dark-green}{HTML}{005953}
+%% Hyperlink colouring
+\hypersetup{
+ colorlinks=true,
+ linkcolor=st-a-blue,
+ filecolor=st-a-burgundy,
+ urlcolor=cyan,
+}
+%% Code Listing style
+\lstset{
+ basicstyle=\small\ttfamily,
+ columns=flexible,
+ breaklines=true,
+ backgroundcolor=\color{st-a-gray},
+ numberstyle=\ttfamily,
+ frame=tb
+}
+%% Matriculation Number
+\def\matrno#1{\def\@matrno{#1}}
+\newcommand{\printmatrno}{\@matrno}
+%% Code command listing
+\def\code#1{\colorbox{st-a-gray}{\texttt{#1}}}
+%% File listing
+\def\file#1{\textcolor{st-a-burgundy}{\texttt{#1}}}
+%% Main matter header style
+\pagestyle{fancy}
+\rhead{\printmatrno}
+\renewcommand{\sectionmark}[1]{\markright{#1}} %% Suppress section numbering in lhead
+%% Define default document text colour
+\color{st-a-black}
+%% Title page
+\def\@maketitle{
+ \pagestyle{empty}
+ \null
+ \vspace{0.3\textheight}
+ \begin{center}
+ {\LARGE\bfseries \@title \par}
+ \vskip 1.5em
+ {\Large \@matrno}
+ \vskip 1.em
+ {\Large \@date}
+ \end{center}
+ \newpage
+ \clearpage
+ \pagenumbering{arabic}
+}
+% Table of Contents
+\renewcommand\tableofcontents{
+ \null\hfill\textbf{\Large\contentsname}\hfill\null\par
+ \@mkboth{\MakeUppercase\contentsname}{\MakeUppercase\contentsname}%
+ \thispagestyle{empty}
+ \@starttoc{toc}%
+ \newpage
+ \clearpage
+ \pagenumbering{arabic}
+}