Things that are nice to look at and how to draw them



We're going to be drawing things. So first, let's choose how we're going to represent points, lines and figures.

Representing Points in Python

We choose to represent a point in the plane as a list [x, y] containing the x and y coordinates of the point. For example the point A = (3, 4) in the plane will be represented by the list [3,4] in python.

Representing Lines in Python

We choose to represent a line as a list of two points [p, q] containing the endpoints of the line. So for example the segment AB between points A = (0,0) and B = (1,1) would be represented in python as the list [[0,0], [1,1]].


In the following exercice, you'll be asked to return a list of lines.

Dragon Curve

The dragon curve is a figure that appeared on the chapter covers of Jurassic Parc. It looks like this :

The 0-dragon curve between two points P and Q is a simple line between the points:

The 1-dragon curve between two points P and Q looks like this :

Recursive definition of the dragon curve

To build the n-dragon curve between points P and Q, you need to define an intermediary point R. Here is how you get R. You build a circle with diameter PQ. If you pick any point A on that circle, the triangle PAQ will be a right triangle. There are two points on that circle that will not only give you a right triangle, but also an isoscele triangle. Pick one of those points call it R and write a function gimmeR(P, Q) that returns the point R. Look at the figure below to better understand.

<img src = "pqr.png" width = "300px">

In [ ]:
def gimmeR(P, Q):
    pass


gimmeR([0,0], [1, 1]) # should return [1, 0]
gimmeR([0,0], [0,1]) # should return [0.5, 0.5]

The n-dragon curve between the points P and Q is defined as the concatenation of the (n-1)-dragon curve between P and R and the (n-1)-dragon curve between Q and R. Using gimmeR, write a recusive function dragon(n, P, Q) that returns the list of segments in the n-dragon curve between P and Q.

In [ ]:
def dragon(n, P, Q):
    pass

The function draw(L) takes a list of lines and draws it. Check that your function works by calling it.

In [62]:
import matplotlib.pyplot as plt
from matplotlib.patches import Circle, Polygon
from matplotlib.collections import PatchCollection, LineCollection

    
def draw(L):
    plt.style.use('ggplot')
    f, ax = plt.subplots()
    lc = LineCollection(L)
    ax.add_collection(lc)
    ax.autoscale()
    ax.set_aspect('equal')
    plt.axis('off')    
    plt.show()

Dragon curves start looking nice and pretty after 10. Here is what a 13-dragon curve looks like :

<img src = "d13.png" width = "300px">

Sierpinsky triangles

Representing Triangles in Python

We choose to represent a triangle as a list of three points [p, q, r]. The following exercice asks you to return a list of triangles, you should return a list containing lists of three points.

Exercice

Here are examples of the Sierpinsky triangles :

The triangles are equilateral. Write a recursive function sierpinsky(p, q, r, n) that takes three points p, q, r (the endpoints of an equilateral triangle), an integer n and returns the list of triangles that make up the n-th sierpinsky triangle.

In [ ]:
import numpy as np
import matplotlib.pyplot as plt


def sierpin(p, q, r, n):
    pass


### p, q and r below are the endpoints of an equilateral triangle, use them to test your code

p = np.array([0,0])
q = np.array([2,0])
r = np.array([1, 3**0.5])

L = sierpin(p, q, r, 4)


def drawTriangles(L):
    triangles = []
    for a, b, c in L:
        triangles += [Polygon([a, b, c], True)]
    f, ax = plt.subplots()
    ax.add_collection(PatchCollection(triangles))
    ax.autoscale()
    ax.set_aspect('equal')
    plt.axis('off')
    plt.show()

drawTriangles(L)

The Ethiopian-Orthodox Cross (Hard)



The Ethiopian-Orthodox cross can be approximated by a fractal. We're going to try drawing this :

<img src = "etcr.png" width = "500px">

The base figure for the cross looks like this :

<img src = "base.png" width = "200px">

You are provided with a function that gives the set of lines that make up this figure. base_figure(scale) returns the base figure centered at the origin (0,0) and scaled by a factor of scale.

The recursive definition :

The n-cross is composed of 8 (n-1)-crosses. Look at the figures below and try finding how you should combine the 8 crosses :


0-cross

1-cross

2-cross

You are provided with the functions :

  • `rotfigure(figure, angle)` that rotates a given figure
  • `shiftfigure(figure, angle, dist)` that shifts a given figure by a value of `dist` in the direction given by `angle`
  • `rescale(figure, factor)` that rescales a figure
The angles are in radians
Use these function to draw the ethiopian cross. (Use trial and error to figure out scaling and shifting parameters)

In [64]:
import numpy as np
import matplotlib.pyplot as plt

def rot(angle, x):
    O = np.array([[np.cos(angle), -1*np.sin(angle)], [np.sin(angle), np.cos(angle)]])
    return np.dot(O, x)

def shift(angle, dist, x):
    vect = dist * np.array([np.cos(angle), np.sin(angle)])
    return np.array(x) + vect

def rotfigure(figure, angle):
    return [[rot(angle, p[0]), rot(angle, p[1])] for p in figure]

def shiftfigure(figure, angle, dist):
    return [[shift(angle, dist, p[0]), shift(angle, dist, p[1])] for p in figure]

def rescale(figure, factor):
    return [[factor*np.array(p[0]), factor*np.array(p[1])] for p in figure]

def base_figure(scale):
    origin = np.array([0,0])
    pi = np.pi
    r = 4
    v_id = np.array([1,1])
    up = np.array([0,1])
    right = np.array([1,0])
    lst = []
    lst.append([origin, origin+scale*v_id])
    lst.append([origin+scale*v_id, origin+scale*v_id + r*scale*right])
    lst.append([origin+scale*v_id + r*scale*right, origin+scale*v_id + r*scale*right + scale*rot(-pi/2, v_id)])
    lst.append([origin+scale*v_id + r*scale*right, origin+scale*v_id + r*scale*right + r*scale*up])
    lst.append([origin+scale*v_id + r*scale*right + r*scale*up, origin+scale*v_id + r*scale*right + r*scale*up + scale*v_id])
    lst.append([origin+scale*v_id , origin+scale*v_id + r*scale*up])
    lst.append([origin+scale*v_id + r*scale*up, origin+scale*v_id + r*scale*up + r*scale*right])
    lst.append([origin+scale*v_id + r*scale*up, origin+scale*v_id + r*scale*up + scale*rot(pi/2, v_id)])
    #center
    center = scale*(1+r/2)*np.array([1,1])
    lst = [ [p[0] - center , p[1] - center ] for p in lst ]
    return [[rot(pi/4, p[0]), rot(pi/4, p[1])] for p in lst]


def ethiodraw(L):
    for pair in L:
        a, b = pair
        line = plt.Line2D([a[0], b[0]], [a[1], b[1]], lw=2.5, color='k')
        plt.gca().add_line(line)
    plt.axis('scaled')
    plt.axis('off')
    #plt.savefig('et0.png', format='png', dpi=300)
    plt.show()
In [ ]:
### Write your function here
def cross(scale, n):
    pass