| Download
Project: Peter's Files
Views: 3893Visibility: Unlisted (only visible to those who know the link)
Image: ubuntu1804# ___ _____ ____ _____ _ _ ____ _1# |_ _| ___/ ___| | ___| __ __ _ ___| |_ __ _| | / ___| ___ _ __ ___ _ __ __ _| |_ ___ _ __2# | || |_ \___ \ | |_ | '__/ _` |/ __| __/ _` | | | | _ / _ \ '_ \ / _ \ '__/ _` | __/ _ \| '__|3# | || _| ___) | | _|| | | (_| | (__| || (_| | | | |_| | __/ | | | __/ | | (_| | || (_) | |4# |___|_| |____/___|_| |_| \__,_|\___|\__\__,_|_|___\____|\___|_| |_|\___|_| \__,_|\__\___/|_|5#678# Load Etxternal packages9import numpy as np # for wicked fast arrays MORE INFORMATION: https://docs.scipy.org/doc/10from numpy import * # for ease of math function use11from numpy.linalg import * # for ease of linalg function use12from numba import njit # for compiling functions into C, so they run faster. MORE INFORMATION: http://numba.pydata.org/13from numba import jit14import matplotlib.pyplot as plt # for simple plotting15from matplotlib import collections as mc # for simple plotting16import random # for random numbers17import time # for timing18from termcolor import colored # for colored print statements19import os # for deep file manipulation20import imageio # for gif making21from matplotlib.ticker import FormatStrFormatter # for FormatStrFormatter22from typing import List # for specification of types23from PIL import Image # for faster images24import math # for math25from scipy.stats import linregress # for linear regressions262728293031## Math Ops32@njit33def opNorm(A):34G = A[:2].T[:2].T35return np.sqrt(np.max(np.linalg.eig(G @ G.T)[0]))36def check_transformations(transformations):37failed = []38for i in np.arange(len(transformations)):39if opNorm(transformations[i]) >= 1:40failed = failed + [i+1]41if len(failed) == 0:42print(colored('The opNorm of every transformation is less than 1 so all of the transformations are contraction mappings.','green'))43elif len(failed) == 1:44print(colored(f'The opNorm of transformation {failed[0]} is greater than or equal to 1 so is not a contraction mapping.','red'))45elif len(failed) > 1:46print(colored(f'The opNorm of transformations {failed} are greater than or equal to 1 so are not contraction mappings.','red'))47@njit48def choose_random_index(weights):49r = random.uniform(0, 1)50t = 051sum = weights[0]52while r > sum:53t += 154sum = sum + weights[t]55return min(t, len(weights)-1)565758596061626364656667## Built-In Transformations68def Scale(s):69return np.array([[s, 0, 0],[0, s, 0],[0, 0, 1]], dtype=np.float64)70def Translate(a, b):71return np.array([[1, 0, a],[0, 1, b],[0, 0, 1]], dtype=np.float64)72def Rotate(theta):73return np.array([[np.cos(theta), -np.sin(theta), 0],[np.sin(theta), np.cos(theta), 0],[0, 0, 1]], dtype=np.float64)74def ShearX(t):75return np.array([[1, t, 0],[0,1, 0],[0, 0, 1]], dtype=np.float64)76def ShearY(t):77return np.array([[1, 0, 0],[t,1, 0],[0, 0, 1]], dtype=np.float64)78def ScaleX(s):79return np.array([[s, 0, 0],[0, 1, 0],[0, 0, 1]], dtype=np.float64)80def ScaleY(s):81return np.array([[1, 0, 0],[0, s, 0],[0, 0, 1]], dtype=np.float64)82def ScaleXY(s, t):83return np.array([[s, 0, 0],[0,t, 0],[0, 0, 1]], dtype=np.float64)848586## Built-In Figures87Box = np.array([ [0., 0., 1.], [1., 0., 1.], [1., 1., 1.], [0., 1., 1.], [0., 0., 1.], [1/8, 1/8, 1.], [1/8-1/16, 1/8+1/16, 1.] ]).T88def rect(n):89return ScaleY(1/n) @ Box90Line = np.array([ [0., 0., 1.], [1., 0., 1.] ]).T9192XBox = np.array([ [0., 0., 1.], [1., 0., 1.], [1., 1., 1.], [0., 1., 1.], [0., 0., 1.], [0.5, 0., 1.], [0.5, 1., 1.], [1., 1., 1.], [1., 0.5, 1.], [0., 0.5, 1.], [0., 0., 1.], [1/8, 1/8, 1.], [1/8-1/16, 1/8+1/16, 1.]]).T93949596979899100101## for Iterating Figures102def transform(figures, transformations): # takes a list of 3xW numpy arrays and a list of transformations (2D 3x3 numpy arrays)103newFigures = [np.array([[1.,0.,1.],[0.,1.,1.]]).T]104for M in figures:105for T in transformations:106newFigures = newFigures + [T @ M]107return newFigures[1:] # returns the list containing each original 3xW numpy array multiplied by each transformation108def generate_figures(n, figures, transformations): # takes a natural number n, a list of 3xW numpy arrays and a list of transformations (2D 3x3 numpy arrays)109if n == 0:110return transform(figures, [np.array([[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]])])111else:112outputFigures = transform(figures, transformations)113for i in range(1,n):114outputFigures = transform(outputFigures, transformations)115return outputFigures # returns the nth iteration of the transform(), a liste116117118119120121122123124## the Fractal class125class Fractal(object):126def __init__(self, transformations, weights=np.array([0.]), size=5, color=(0,0,255)):127self.transformations = transformations128self.color = color129if all(weights == np.array([0.])):130self.weights = np.array([1/len(transformations)]*len(transformations))131else:132self.weights = weights133134self.size = size135self.xmin, self.xmax, self.ymin, self.ymax = find_bounds(self.transformations,self.weights)136self.bounds = (self.xmin, self.xmax, self.ymin, self.ymax)137if self.xmax-self.xmin==0 or self.ymax-self.ymin==0:138raise ValueError('Fractal converges to point.')139self.width = math.ceil((self.xmax-self.xmin)*36*self.size)140self.height = math.ceil((self.ymax-self.ymin)*36*self.size)141self.isZoomed = False142143self.point = np.array([0,0,1])144self.developement = 0145self.pic = Image.new('RGB', (self.width, self.height), (255, 255, 255))146self.pixels = self.pic.load()147148def _scale(self, point):149h = self.width * (point[0]-self.xmin)/(self.xmax-self.xmin) # (x + 2.182)*(self.width - 1)/4.8378 Why take an input?150k = self.height * (self.ymax-point[1])/(self.ymax-self.ymin) # (9.9983 - y)*(self.height - 1)/9.9983151return h, k152153def _iterate(self):154r = random.uniform(0, 1)155t = 0156sum = self.weights[0]157while r > sum:158t += 1159sum += self.weights[t]160self.point = self.transformations[min(t, len(self.weights)-1)] @ self.point161162def add_points(self, n):163for _ in range(100):164self._iterate()165for _ in range(n):166self._iterate()167self.pixels[self._scale(self.point)] = self.color168self.developement += n169170def set_zoom(self, isZoomed=True, xmin=None, xmax=None, ymin=None, ymax=None):171self.isZoomed = isZoomed172if xmin is not None:173self.xmin = xmin174if xmax is not None:175self.xmax = xmax176if ymin is not None:177self.ymin = ymin178if ymax is not None:179self.ymax = ymax180181def add_points_zoom(self, n, x, y):182self.point = np.array([x,y,1])183for _ in range(100):184self._iterate()185while (self.xmin>self.point[0]) or (self.xmax<self.point[0]) or (self.ymin>self.point[1]) or (self.ymax<self.point[1]):186self._iterate()187for _ in range(n):188self._iterate()189while (self.xmin>self.point[0]) or (self.xmax<self.point[0]) or (self.ymin>self.point[1]) or (self.ymax<self.point[1]):190self._iterate()191self.pixels[self._scale(self.point)] = self.color192193def make_gif(self, name='GIF', n=100_000, zoom=2, frames=7, zoomPoint=None):194start = time.time()195print(f'Generating {frames} Zoomed images: ', end='')196if zoomPoint is None:197x, y = (self.xmax + self.xmin)/2, (self.ymax + self.ymin)/2198else:199x, y = zoomPoint200for frame in range(frames+1):201step = Fractal(self.transformations, self.weights, self.size, self.color)202scale = 1 - ((zoom-1)/zoom)*sqrt(frame/frames)203print(f'{frame}({round(1/scale, 2)}) ', end='')204xminZ = x - (self.xmax - self.xmin) * scale / 2205xmaxZ = x + (self.xmax - self.xmin) * scale / 2206yminZ = y - (self.ymax - self.ymin) * scale / 2207ymaxZ = y + (self.ymax - self.ymin) * scale / 2208step.set_zoom(True, xminZ, xmaxZ, yminZ, ymaxZ)209step.add_points_zoom(n, x, y)210step.save_pic(f'Saved Fractals/For Zooms/{name}_{frame}.png')211images = [imageio.imread(f'Saved Fractals/For Zooms/{name}_0.png')]*frames212for frame in range(frames+1):213images.append(imageio.imread(f'Saved Fractals/For Zooms/{name}_{frame}.png'))214os.remove(f'Saved Fractals/For Zooms/{name}_{frame}.png')215imageio.mimsave(f'Saved Fractals/{name}.gif', images)216end = time.time()-start217print(f'\nCompleted in {int(end//60)} minutes and {round(end%60)} seconds.')218219def load_in_points(self, externalTup, frame=None):220n = len(externalTup[0])221externalArray = np.array([*externalTup,[1.]*n]).T222if frame is None:223for row in externalArray:224self.pixels[self._scale(row)] = self.color225self.developement += n226else:227for row in externalArray:228if (frame[0]<=row[0]) and (frame[1]>=row[0]) and (frame[2]<=row[1]) and (frame[3]>=row[1]):229self.pixels[self._scale(row)] = self.color230self.developement += n231232def save_pic(self, path='Trash.png'):233self.pic.save(path)234235def display_pic(self):236# https://stackoverflow.com/a/26649884237plt.imshow(np.asarray(self.pic))238239def dark_pixels(self):240return np.count_nonzero(255 - np.asarray(self.pic)) // 3241242def dimension(self, n=3_000_000, startSize=2, endSize=100, samples=None):243start = time.time()244test = Fractal(self.transformations, weights=self.weights, size = endSize, color=(0,0,0))245G = _generate_points_full(n, self.transformations, weights=self.weights)246test.load_in_points(G)247tag = str(time.time())248tag = tag[:10] + '_' + tag[11:]249test.pic.save(f'DIMENSION_CALCULATION_MAX_SIZE_CHECK_{tag}.png')250print(f'Verify that the image of the fractal with {n} points is \'filled in\' at size {endSize}. (Image file is in parent directory).')251elapsed = time.time() - start252go = input("Press Enter to continue or type stop.")253middle = time.time()254if (go == 'stop') or (go == 'STOP') or (go == 'Stop'):255print('Dimension calculation terminated.')256os.remove(f'DIMENSION_CALCULATION_MAX_SIZE_CHECK_{tag}.png')257return None258os.remove(f'DIMENSION_CALCULATION_MAX_SIZE_CHECK_{tag}.png')259print('Computing fractal dimension ', end='')260# find samples261if samples is None:262samples = endSize - startSize263for j in range(endSize - startSize):264samples -= j265potentialArray = np.floor(np.geomspace(startSize, endSize, num=samples))266if potentialArray[0] != potentialArray[1]:267break268print(f'with {samples} samples...')269numberOfPixelsDark = np.array([0.]*samples)270numberOfPixelsDark[samples - 1] = test.dark_pixels()271# https://docs.scipy.org/doc/numpy/reference/generated/numpy.geomspace.html#numpy.geomspace272scalingFactor = np.floor(np.geomspace(startSize, endSize, num=samples))273for i in range(samples-1):274if scalingFactor[i] == scalingFactor[i-1]:275numberOfPixelsDark[i] = numberOfPixelsDark[i-1]276continue277test = Fractal(self.transformations, weights=self.weights, size=scalingFactor[i], colour=(0,0,0))278test.load_in_points(G)279numberOfPixelsDark[i] = test.dark_pixels()280if i == 0:281print(f'Finished samples with sizes: {int(np.floor(startSize))}', end='')282elif i == samples - 2:283print(f', {int(scalingFactor[i])}, {int(scalingFactor[i+1])}.')284else:285print(f', {int(scalingFactor[i])}', end='')286print(f'Completed in {np.round((elapsed + time.time() - middle)/60, decimals = 1)} minutes.')287return np.round(linregress(np.log(scalingFactor), np.log(numberOfPixelsDark))[0], decimals = 2)288289290291292## for iterating points (and saving the array of points)293def generate_points(n, transformations, weights=np.array([0.]), startingPoint=np.array([0.,0.,1.]), frame=np.array([0.])):294start = time.time()295print(f'Generating {n} points...', end='')296if all(frame == np.array([0.])):297G = _generate_points_full(n, transformations, weights, startingPoint)298else:299startingPoint = np.array([(frame[1]-frame[0])/2,(frame[3]-frame[2])/2,1.])300G = _generate_points_zoom(n, transformations, weights, startingPoint, frame)301print(f' Finished in {time.time()-start} seconds.')302return G303304@njit305def _generate_points_full(n, transformations, weights=np.array([0.]), startingPoint=np.array([0.,0.,1.])):306if all(weights == np.array([0.])):307return _generate_points_full_simple(n, transformations, startingPoint)308output = np.array([[startingPoint[0],startingPoint[1],1.]]*n)309for i in range(100):310output[0] = transformations[choose_random_index(weights)] @ output[0]311for i in range(1,n):312output[i] = transformations[choose_random_index(weights)] @ output[i-1]313return (output[:,0],output[:,1])314315@njit316def _generate_points_full_simple(n, transformations, startingPoint=np.array([0.,0.,1.])):317output = np.array([[startingPoint[0],startingPoint[1],1.]]*n)318for _ in np.arange(100):319output[0] = transformations[random.randint(0,len(transformations)-1)] @ output[0]320for i in np.arange(1,n):321output[i] = transformations[random.randint(0,len(transformations)-1)] @ output[i-1]322return (output[:,0], output[:,1])323324@njit325def _generate_points_zoom(n, transformations, weights=np.array([0.]), startingPoint=np.array([0.,0.,1.]), frame=np.array([0.])):326if all(weights == np.array([0.])):327return generate_points_zoom_simple(n, transformations, startingPoint, frame)328if all(frame == np.array([0.])): # this is to keep the order of function inputs consistant329print('THERE WAS AN ERROR: _generate_points_zoom was not given a zoom frame.')330output = np.array([[startingPoint[0],startingPoint[1],1]]*n, dtype=np.float64)331for _ in range(20):332potentialPoint = transformations[choose_random_index(weights)] @ outputFigures[i-1]333while (frame[0]>potentialPoint[0]) or (frame[1]<potentialPoint[0]) or (frame[2]>potentialPoint[1]) or (frame[3]<potentialPoint[1]):334potentialPoint = transformations[choose_random_index(weights)] @ potentialPoint335output[0] = potentialPoint336for i in np.arange(1,n):337potentialPoint = transformations[choose_random_index(weights)] @ outputFigures[i-1]338while (frame[0]>potentialPoint[0]) or (frame[1]<potentialPoint[0]) or (frame[2]>potentialPoint[1]) or (frame[3]<potentialPoint[1]):339potentialPoint = transformations[choose_random_index(weights)] @ potentialPoint340output[i] = potentialPoint341return (output[:,0], output[:,1])342343@njit344def _generate_points_zoom_simple(n, transformations, startingPoint, frame):345outputFigures = np.array([[startingPoint[0],startingPoint[1],1.]]*n)346for _ in range(20):347potentialPoint = transformations[random.randint(0,len(transformations)-1)] @ potentialPoint348while (frame[0]>potentialPoint[0]) or (frame[1]<potentialPoint[0]) or (frame[2]>potentialPoint[1]) or (frame[3]<potentialPoint[1]):349potentialPoint = transformations[random.randint(0,len(transformations)-1)] @ potentialPoint350output[0] = potentialPoint351for i in np.arange(1,n):352potentialPoint = transformations[random.randint(0,len(transformations)-1)] @ potentialPoint353while (frame[0]>potentialPoint[0]) or (frame[1]<potentialPoint[0]) or (frame[2]>potentialPoint[1]) or (frame[3]<potentialPoint[1]):354potentialPoint = transformations[random.randint(0,len(transformations)-1)] @ potentialPoint355output[i] = potentialPoint356return (output[:,0], output[:,1])357358359@njit360def find_bounds(transformations, weights=np.array([0.])):361G = _generate_points_full(100000, transformations=transformations, weights=weights)362xmin, xmax, ymin, ymax = np.min(G[0]), np.max(G[0]), np.min(G[1]), np.max(G[1])363errX, errY = (xmax-xmin)/10, (ymax-ymin)/10364return np.array([xmin-errX, xmax+errX, ymin-errY, ymax+errY], dtype=np.float64)365366367## Simple plots and saves368def plot_figures(figuresToPlot:List[np.ndarray], size:float=5, width:float=1 , colour:str='blue', path:str="TRASH.png"):369lines = [ [(1.1, 1.1), (1.1, 1.1)] ]370for figure in figuresToPlot:371lines = lines + [[ (figure[0][i], figure[1][i]), (figure[0][i+1], figure[1][i+1]) ] for i in range(len(figure.T)-1)]372lc = mc.LineCollection(lines[1:], linewidths=width, color=colour)373fig, ax = plt.subplots(figsize=(size,size))374ax.add_collection(lc)375ax.set_aspect('equal')376ax.autoscale()377plt.savefig(path, bbox_inches='tight', dpi=144, format='png')378plt.show()379def plot_points(points, size=5, colour="blue", path='TRASH.png'):380fig, ax = plt.subplots(figsize=(size,size))381ax.set_aspect('equal')382ax.plot(*points, ',', color=colour)383plt.axis('off')384plt.savefig(path, bbox_inches='tight', dpi=144, format='png')385plt.axis('on')386# num, KCnum, (X, Y) = pixel_data(path)387plt.show()388# print(f'{num} pixels are dark ({num*100/KCnum} percent)')389def save_points(points, size=5, colour="blue", path='TRASH.png'):390if path == 'TRASH.png':391print(colored('No Path Specified!','red'))392return None393fig, ax = plt.subplots(figsize=(size,size))394ax.set_aspect('equal')395ax.plot(*points, ',', color=colour)396plt.axis('off')397ax.yaxis.set_major_formatter(FormatStrFormatter('%.3f')) # https://stackoverflow.com/a/29188910/6504760398ax.xaxis.set_major_formatter(FormatStrFormatter('%.3f'))399plt.savefig(path, bbox_inches='tight', dpi=144, format='png');400fig.clear()401plt.close(fig)402403404405406407408409410411412413414415## Word Fractals416417def word_fractal(string):418n = len(string)419gap = 1/(n**2)420width = (1 - (n-1)*gap)/n421macros = [eval('_'+letter)(n) for letter in string.upper()]422transformations = []423for i in range(n):424letter = macros[i]425for t in range(len(letter)):426transformations = transformations + [Translate(i*(width + gap),0) @ ScaleXY(width, 1/n) @ letter[t]]427return transformations428429def _(n):430return []431def _a(n):432theta = pi/2.3433phi = pi/2 - theta434h = (n-cos(theta))/(n*sin(theta))435ell = (h*(n+1)*cos(theta))/(2*n-2*cos(theta))436b = sin(theta)/n + ell437X1 = h*cos(theta) + sin(theta)/n - cos(theta)/n438Y1 = 1 - sin(theta)/n - cos(theta)/n439c = (1 - 1/n - cos(theta)/(2*n) -X1)/sin(theta)440return [Translate(sin(theta)/n,0) @ Rotate(theta) @ ScaleX(h),441Translate(b,1/2-1/(2*n)) @ ScaleX(1-b-1/n),442Translate(X1,Y1) @ Rotate(-phi) @ ScaleX(c),443Translate(1-1/n,Y1-c*cos(theta)+sin(theta)/(2*n)) @ Rotate(-pi/2) @ ScaleX(Y1-c*cos(theta)+sin(theta)/(2*n))]444def _A(n):445return [ShearY((1-1/n)*2) @ ScaleX(1/2),446Translate(1/2, 1-1/n) @ ShearY((1/n-1)*2) @ ScaleX(1/2),447Translate(n/(4*(n-1)),1/2-1/n) @ ScaleX(1 - n/(2*(n-1)))]448def _B(n):449return [Translate(0,1) @ Rotate(-pi/2),450Translate(1/n, 1-1/(2*n)) @ ScaleXY(1/2 - 1/(2*n),1/2),451Translate(1/2 + 1/(2*n), 1/2+1/(4*n)) @ Rotate(pi/2) @ ScaleXY(1/2 - 3/(4*n),1/2),452Translate(1/n,1/2 - 1/(4*n)) @ ScaleXY(1-2/n, 1/2),453Translate(1-1/n, 1/2+1/(4*n)) @ Rotate(-pi/2) @ ScaleX(1/2 - 3/(4*n)),454Translate(1/n,0) @ ScaleX(1 - 1/n)]455def _C(n):456return [Translate(1/n,0) @ ScaleX(1-1/n),457Translate(1/n,0) @ Rotate(pi/2),458Translate((1/n),1-1/n) @ ScaleX(1-1/n)]459def _D(n):460return [Translate(0,1) @ Rotate(-pi/2),461Translate(1/n,1-1/n) @ ShearY(-1/2) @ ScaleX(1-2/n),462Translate(1-1/n, 1/2+1/n) @ Rotate(-pi/2) @ ScaleX(1/2+1/n),463Translate(1/n,0) @ ScaleX(1-2/n)]464def _E(n):465return [Translate(0,1) @ Rotate(-pi/2),466Translate(1/n,1-1/n) @ ScaleX(1-1/n),467Translate(1/n,1/2-1/(4*n)) @ ScaleXY(1-2/n,1/2),468Translate(1/n,0) @ ScaleX(1-1/n)]469def _F(n):470return [Translate(0,1) @ Rotate(-pi/2),471Translate(1/n,1-1/n) @ ScaleX(1-1/n),472Translate(1/n,1/2-1/(4*n)) @ ScaleXY(1-2/n,1/2)]473def _G(n):474return [Translate(1/n,0) @ ScaleX(1-1/n),475Translate(1/n,0) @ Rotate(pi/2),476Translate((1/n),1-1/n) @ ScaleX(1-1/n),477Translate(1-1/n,1/2) @ Rotate(-pi/2) @ ScaleX(1/2 - 1/n),478Translate(1-1/n-1/4,1/2 - 1/(2*n)) @ ScaleXY(1/4,1/2)]479def _H(n):480return [Translate(1/n,0) @ Rotate(pi/2),481Translate(1,0) @ Rotate(pi/2),482Translate(1/n,1/2-1/(2*n)) @ ScaleX(1-2/n)]483def _I(n):484return [Translate(0,1-1/n),485Translate(1/2 - 1/(2*n),1-1/n) @ Rotate(-pi/2) @ ScaleX(1-2/n),486Scale(1)]487def _J(n):488return [Translate(0,1-1/n),489Translate(1/2 - 1/(2*n),1-1/n) @ Rotate(-pi/2) @ ScaleX(1-2/n),490ScaleX(1/2 + 1/(2*n))]491def _K(n):492return [Translate(0,1) @ Rotate(-pi/2),493Translate(1/n,1/2) @ ShearY((n/2 - 1)/(n-1)) @ ScaleX(1-1/n),494Translate(1/n,1/2-1/n) @ ShearY((1 - n/2)/(n-1)) @ ScaleX(1-1/n)]495def _L(n):496return [Translate(0,1) @ Rotate(-pi/2),497Translate(1/n,0) @ ScaleX(1-1/n)]498def _M(n):499return [Translate(0,1) @ Rotate(-pi/2),500Translate(1/n,1-1/n) @ ShearY(-1) @ ScaleX(1/2 - 1/n),501Translate(1/2,1/2) @ ShearY(1) @ ScaleX(1/2 - 1/n),502Translate(1-1/n,1) @ Rotate(-pi/2)]503def _N(n):504return [Translate(0,1) @ Rotate(-pi/2),505Translate(1/n,1-1/n) @ ShearY((1/n-1)/(1-2/n)) @ ScaleX(1 - 2/n),506Translate(1-1/n,1) @ Rotate(-pi/2)]507def _O(n):508return [Translate(1/n,0) @ ScaleX(1-2/n),509Translate(1,0) @ Rotate(pi/2),510Translate(1/n,1-1/n) @ ScaleX(1-2/n),511Translate(1/n,0) @ Rotate(pi/2)]512def _P(n):513return [Translate(0,1) @ Rotate(-pi/2),514Translate(1/n, 1-3/(4*n)) @ ScaleXY(1-2/n,3/4),515Translate(1-1/n, 1) @ Rotate(-pi/2) @ ScaleX(1/2),516Translate(1/n, 1/2) @ ScaleXY(1-2/n,3/4)]517def _Q(n):518theta = arctan(1 - 2/n)519z = (1/2 - 1/n - sin(theta)/n)*cos(theta)520x = 1/2 - z*sin(theta) - cos(theta)/n521y = x*tan(theta)522return [Translate(0,1/2) @ ShearY(1-2/n) @ ScaleX(1/2),523Translate(0,1/2-1/n) @ ShearY(2/n-1) @ ScaleX(1/2),524Translate(1/2,0) @ ShearY(1-2/n) @ ScaleX(1/2),525Translate(1/2,1-1/n) @ ShearY(2/n-1) @ ScaleX(1/2),526Translate(1/2 + x,y) @ Rotate(theta-pi/2) @ ScaleXY(z,1/2)]527def _R(n):528return [Translate(0,1) @ Rotate(-pi/2),529Translate(1/n, 1-3/(4*n)) @ ScaleXY(1-2/n,3/4),530Translate(1-1/n, 1) @ Rotate(-pi/2) @ ScaleX(1/2),531Translate(1/n, 1/2) @ ScaleXY(1-2/n,3/4),532Translate(1/n,1/2-1/n) @ ShearY((1/n-1/2)/(1-1/n)) @ ScaleX(1-1/n)]533def _S(n):534return [ScaleX(1-1/n),535Translate(1,0) @ Rotate(pi/2) @ ScaleX(1/2-1/(2*n)),536Translate(1/n,1/2-1/(2*n)) @ ScaleX(1-1/n),537Translate(1/n,1/2-1/(2*n)) @ ScaleY(1/2-1/(2*n)) @ Rotate(pi/2),538Translate(0,1-1/n)]539def _T(n):540return [Translate(0,1-1/n),541Translate(1/2 - 1/(2*n),1-1/n) @ Rotate(-pi/2) @ ScaleX(1-1/n)]542def _U(n):543return [Scale(1),544Translate(0,1) @ Rotate(-pi/2) @ ScaleX(1-1/n),545Translate(1-1/n,1) @ Rotate(-pi/2) @ ScaleX(1-1/n)]546def _V(n):547return [Translate(0,1-1/n) @ ShearY(2/n-2) @ ScaleX(1/2),548Translate(1/2,0) @ ShearY(2-2/n) @ ScaleX(1/2)]549def _W(n):550return [Translate(0,1) @ Rotate(-pi/2),551Translate(1/n,0) @ ShearY(1) @ ScaleX(1/2 - 1/n),552Translate(1/2,1/2-1/n) @ ShearY(-1) @ ScaleX(1/2 - 1/n),553Translate(1-1/n,1) @ Rotate(-pi/2)]554def _X(n):555return [Translate(cos(pi/4)/n,0) @ Rotate(pi/4) @ ScaleX(sqrt(2)-1/n),556Translate(0,1-cos(pi/4)/n) @ Rotate(-pi/4) @ ScaleX(cos(pi/4) - 1/n),557Translate(1/2,1/2-cos(pi/4)/n) @ Rotate(-pi/4) @ ScaleX(cos(pi/4) - 1/n)]558def _Y(n):559return [Translate(1/2-1/(2*n),1/2) @ Rotate(-pi/2) @ ScaleX(1/2),560Translate(0,1-1/n) @ ShearY((1/2)/(1/(2*n) - 1/2)) @ ScaleX(1/2 - 1/(2*n)),561Translate(1/2 + 1/(2*n),1/2 - 1/n) @ ShearY((1/2)/(1/2 - 1/(2*n))) @ ScaleX(1/2 - 1/(2*n))]562def _Z(n):563return [Translate(0,1-1/n),564Translate(1/n,1/n) @ Rotate(pi/2) @ ShearY(1/(1/n-1)) @ ScaleX(1-2/n),565Scale(1)]566567568569570571572573574575576577578579580581582######### Pre load njit functions ?583584585586587588589590591