├── .gitignore ├── LICENSE ├── README.md ├── common.py ├── mkvideo.sh ├── more └── plasma_spiral.py ├── p01.png ├── p01.py ├── p02.png ├── p02.py ├── p03.png ├── p03.py ├── p04.png ├── p04.py ├── p05.png ├── p05.py ├── p06.png ├── p06.py ├── p07.png ├── p07.py ├── p08.png ├── p08.py ├── p09.png ├── p09.py ├── p10.png ├── p10.py ├── p11.png ├── p11.py ├── p12.png ├── p12.py ├── p13.png ├── p13.py ├── p14.png ├── p14.py ├── p15.png ├── p15.py ├── p16.png ├── p16.py ├── p17.png ├── p17.py ├── p18.png ├── p18.py ├── p19.png ├── p19.py ├── p20.png ├── p20.py ├── p21.png ├── p21.py ├── p22.png ├── p22.py ├── p23.png ├── p23.py ├── p24.png ├── p24.py ├── p25.png ├── p25.py ├── video.py ├── video2.py ├── video3.py ├── video4.py ├── video5.py ├── video6.py ├── video7.py ├── video8.py ├── video8a.py ├── video8b.py └── video9.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | local_settings.py 55 | 56 | # Flask stuff: 57 | instance/ 58 | .webassets-cache 59 | 60 | # Scrapy stuff: 61 | .scrapy 62 | 63 | # Sphinx documentation 64 | docs/_build/ 65 | 66 | # PyBuilder 67 | target/ 68 | 69 | # IPython Notebook 70 | .ipynb_checkpoints 71 | 72 | # pyenv 73 | .python-version 74 | 75 | # celery beat schedule file 76 | celerybeat-schedule 77 | 78 | # dotenv 79 | .env 80 | 81 | # virtualenv 82 | venv/ 83 | ENV/ 84 | 85 | # Spyder project settings 86 | .spyderproject 87 | 88 | # Rope project settings 89 | .ropeproject 90 | 91 | # macOS stuff 92 | .DS_Store 93 | 94 | # video files: 95 | *.mp4 96 | *.ogg 97 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2016, Federico Ferri 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # geometric_patterns 2 | creating artwork with python and numpy 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /common.py: -------------------------------------------------------------------------------- 1 | import math 2 | import numpy as np 3 | from PIL import Image 4 | 5 | class norm: 6 | @staticmethod 7 | def L1(x1,x2): 8 | return np.abs(x1)+np.abs(x2) 9 | @staticmethod 10 | def L2(x1,x2): 11 | return np.sqrt(x1**2+x2**2) 12 | @staticmethod 13 | def Linf(x1,x2): 14 | return np.maximum(np.abs(x1),np.abs(x2)) 15 | 16 | distance = norm 17 | 18 | def meshgrid_euclidean(shape): 19 | return np.meshgrid(*map(range,shape)) 20 | 21 | def meshgrid_distance(shape,center=None,dist=distance.L2): 22 | y,x=meshgrid_euclidean(shape) 23 | if center is None: center=np.array(shape)/2 24 | y,x=y-center[0],x-center[1] 25 | return dist(x,y) 26 | 27 | def meshgrid_polar(shape,center=None,dist=distance.L2): 28 | y,x=meshgrid_euclidean(shape) 29 | if center is None: center=np.array(shape)/2 30 | y,x=y-center[0],x-center[1] 31 | return dist(x,y),np.arctan2(x,y) 32 | 33 | def meshgrid_hyperbolic(shape): 34 | y,x=meshgrid_euclidean(shape) 35 | u=0.5*(np.log(x+1)-np.log(y+1)) 36 | v=np.sqrt(x*y) 37 | return u,v 38 | 39 | def clip(x,xmax): 40 | return np.minimum(xmax-1,np.maximum(0,x)) 41 | 42 | def cycle(x,xmax): 43 | return np.fmod(-np.minimum(0,np.ceil(x/xmax))*xmax+x,xmax) 44 | 45 | def imwarp(im,fn,oob=clip): 46 | warped=np.zeros_like(im) 47 | i,j=meshgrid_euclidean(im.shape) 48 | h,k=fn(i,j) 49 | h,k=oob(h,im.shape[0]),oob(k,im.shape[1]) 50 | h,k=np.int32(h),np.int32(k) 51 | i,j,h,k=map(lambda x: x.reshape(-1), (i,j,h,k)) 52 | warped[i,j]=im[h,k] 53 | return warped 54 | 55 | def imblt(im,op,x,y,srcim,srcx1=0,srcy1=0,srcx2=None,srcy2=None): 56 | srcimsub=srcim[srcy1:srcy2,srcx1:srcx2] 57 | im[y:y+srcimsub.shape[0],x:x+srcimsub.shape[1]]=op(im[y:y+srcimsub.shape[0],x:x+srcimsub.shape[1]],srcimsub) 58 | 59 | def imcircle(im,x,y,r): 60 | x,y,r=map(int,(x,y,r)) 61 | s=meshgrid_distance((2*r,)*2)<=r 62 | imblt(im,np.maximum,int(x-r),int(y-r),s) 63 | 64 | def imtile(im,shape): 65 | im=np.kron(np.ones(tuple(int(0.5+shape[i]/im.shape[i]) for i in range(2)),dtype=np.uint8),im) 66 | return im[0:shape[0],0:shape[1]] 67 | 68 | def checkerboard(shape,sqshape,inv=False): 69 | if isinstance(shape,(int,float))==1: shape=(int(shape),int(shape)) 70 | if isinstance(sqshape,(int,float))==1: sqshape=(int(sqshape),int(sqshape)) 71 | y,x=np.meshgrid(*map(range,shape)) 72 | return (x//sqshape[1]%2)^(y//sqshape[0]%2) 73 | 74 | def box2(shape,delta): 75 | box=np.zeros(shape,dtype=np.uint8) 76 | box[delta[0]:shape[0]-delta[0], delta[1]:shape[1]-delta[1]]=1 77 | return box 78 | 79 | def boxN(shape,n): 80 | box=meshgrid_distance(shape,None,distance.Linf) 81 | l=max(shape)//(n*2) 82 | box=box//l%2 83 | return np.uint8(box) 84 | 85 | def imnormalize(im): 86 | im-=np.min(im) 87 | M=np.max(im) 88 | if M>0: im=im*255/M 89 | return im 90 | 91 | def imshow(im,normalize=True): 92 | if len(im.shape)==2: 93 | if normalize: im=imnormalize(im) 94 | im=np.float32(im) 95 | if len(im.shape)==3 and im.shape[2]==3: 96 | im=np.uint8(im) 97 | im=Image.fromarray(im) 98 | im.show() 99 | 100 | def imsave(im,filename,normalize=True): 101 | if len(im.shape)==2: 102 | if normalize: im=imnormalize(im) 103 | im=Image.fromarray(np.uint8(im)) 104 | im.save(filename) 105 | 106 | def imload(filename): 107 | im=Image.open(filename) 108 | arr=np.asarray(im.getdata()) 109 | arr.resize(im.height, im.width, 3) 110 | return arr 111 | 112 | def apply_colormap(im,cmap,prenormalize=True): 113 | if callable(cmap): cmap=cmap() 114 | if cmap.shape != (256,3): raise ValueError('colormap must be 256x3 uint8 values') 115 | if prenormalize: im=imnormalize(im) 116 | return cmap[np.uint8(im.reshape(-1))].reshape(im.shape+(3,)) 117 | 118 | def make_colormap(colors,positions=None): 119 | if positions is None: positions=np.uint8(np.linspace(0,255,len(colors))) 120 | colors=np.array(colors) 121 | if colors.shape[1] != 3: raise ValueError('colors must be Nx3 uint8 values') 122 | if len(positions) != colors.shape[0]: raise ValueError('positions must be an array of %d floating point values' % colors.shape[0]) 123 | if any(pos < 0 or pos > 255 for pos in positions): raise ValueError('positions must be between 0 and 255') 124 | cmap=np.zeros((256,3), dtype=np.uint8) 125 | for c1,c2,p1,p2 in zip(colors,colors[1:],positions,positions[1:]): 126 | for i in range(p1,p2+1): 127 | x=(i-p1)/(p2-p1) 128 | cmap[i,:]=c1*(1-x)+c2*x 129 | return np.uint8(cmap) 130 | 131 | class colormap: 132 | @staticmethod 133 | def rainbow(): 134 | cc=[[255,0,0],[255,255,0],[0,255,0],[0,255,255],[0,0,255],[255,0,255],[255,0,0]] 135 | pp=[0,25,76,127,178,229,255] 136 | return make_colormap(cc,pp) 137 | @staticmethod 138 | def rainbow2(offset=0.0): 139 | cmap=np.zeros((256,3), dtype=np.uint8) 140 | for i in range(256): 141 | for j in range(3): 142 | cmap[i,j]=127.5*(1+math.cos(offset+math.pi*(i*2/255-2*j/3+0))) 143 | return cmap 144 | @staticmethod 145 | def jet(): 146 | cc=[[0,0,255],[0,255,255],[130,255,130],[255,255,10],[255,0,0],[130,0,0]] 147 | pp=[0,95,125,160,235,255] 148 | return make_colormap(cc,pp) 149 | @staticmethod 150 | def hot(): 151 | cc=[[0,0,0],[255,0,0],[255,255,0],[255,255,255]] 152 | pp=[0,95,185,255] 153 | return make_colormap(cc,pp) 154 | @staticmethod 155 | def cold(): 156 | cc=[[0,0,0],[0,0,255],[0,255,255],[255,255,255]] 157 | pp=[0,95,185,255] 158 | return make_colormap(cc,pp) 159 | @staticmethod 160 | def contours(n,w=1): 161 | cmap=np.zeros((256,3), dtype=np.uint8) 162 | for p in np.linspace(0,255,2+n)[1:-1]: 163 | cmap[int(p-w/2):int(p+w/2)+1,:]=[255,255,255] 164 | return cmap 165 | 166 | def mkline(start, end): 167 | # Bresenham's Line Algorithm 168 | x1, y1 = start 169 | x2, y2 = end 170 | x1, y1, x2, y2 = map(int, (x1, y1, x2, y2)) 171 | dx = x2 - x1 172 | dy = y2 - y1 173 | is_steep = abs(dy) > abs(dx) 174 | if is_steep: 175 | x1, y1 = y1, x1 176 | x2, y2 = y2, x2 177 | swapped = False 178 | if x1 > x2: 179 | x1, x2 = x2, x1 180 | y1, y2 = y2, y1 181 | swapped = True 182 | dx = x2 - x1 183 | dy = y2 - y1 184 | error = int(dx / 2.0) 185 | ystep = 1 if y1 < y2 else -1 186 | y = y1 187 | X, Y = [], [] 188 | for x in range(x1, x2 + 1): 189 | X.append(y if is_steep else x) 190 | Y.append(x if is_steep else y) 191 | error -= abs(dy) 192 | if error < 0: 193 | y += ystep 194 | error += dx 195 | if swapped: 196 | X.reverse() 197 | Y.reverse() 198 | return X, Y 199 | 200 | def imline(im, start, end, value=255, alpha=1.0): 201 | x, y = mkline(start, end) 202 | x, y = x[1:], y[1:] 203 | im[x,y] = alpha*value + (1-alpha)*im[x,y] 204 | 205 | -------------------------------------------------------------------------------- /mkvideo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ffmpeg -framerate 30 -i $1-%08d.png -c:v libx264 -r 30 -pix_fmt yuv420p $1.mp4 3 | -------------------------------------------------------------------------------- /more/plasma_spiral.py: -------------------------------------------------------------------------------- 1 | import math 2 | import numpy as np 3 | from PIL import Image 4 | def ramp(values,positions,n): 5 | r=np.zeros((n,),dtype=np.uint8) 6 | for (vi,vj,pi,pj) in zip(values,values[1:],positions,positions[1:]): 7 | for h in range(pi,1+pj): 8 | a=(h-pi)/(pj-pi) 9 | r[h]=(1-a)*vi+a*vj 10 | return r 11 | im=np.zeros((1024,1024,3),dtype=np.uint8) 12 | cmap=np.zeros((1024,3),dtype=np.uint8) 13 | cmap[...,0]=ramp([20,0,100,50],[0,700,900,1023],1024) 14 | cmap[...,1]=ramp([100,0,100,0],[0,255,700,1023],1024) 15 | cmap[...,2]=ramp([255,0,100,0,255],[0,500,950,1000,1023],1024) 16 | print('im.shape:',im.shape) 17 | for i in range(im.shape[0]): 18 | for j in range(im.shape[1]): 19 | y,x=i-im.shape[0]*0.5,j-im.shape[1]*0.5 20 | r,a=math.hypot(x,y),math.atan2(y,x) 21 | v=1023*0.5*(1.+math.sin((0.003*r)**2+4*a))+(8*a*1024/2/math.pi) 22 | while v<0: v+=1024 23 | while v>=1024: v-=1024 24 | for h in range(3): 25 | im[i,j,h]=cmap[int(v),h] 26 | 27 | im=Image.fromarray(im) 28 | #im.save('my.png') 29 | im.show() 30 | -------------------------------------------------------------------------------- /p01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fferri/geometric_patterns/f10c0781c30282ca517e00ba1ffe887d73f602b1/p01.png -------------------------------------------------------------------------------- /p01.py: -------------------------------------------------------------------------------- 1 | from common import * 2 | 3 | def draw(**kwargs): 4 | s=2**9 5 | k=2 # try 1, 2, 3... 6 | 7 | im=checkerboard(s,s//k) 8 | for i in range(6): 9 | im[[0,-1],...]^=1 10 | im[...,[0,-1]]^=1 11 | s//=2 12 | ch=checkerboard((s,im.shape[1]),s//k) 13 | cv=checkerboard((im.shape[0]+2*s,s),s//k) 14 | im=np.vstack((cv,np.hstack((ch,im,ch)),cv)) 15 | return im 16 | 17 | if __name__ == '__main__': 18 | im=draw() 19 | imshow(im) 20 | imsave(im,'p01.png') 21 | -------------------------------------------------------------------------------- /p02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fferri/geometric_patterns/f10c0781c30282ca517e00ba1ffe887d73f602b1/p02.png -------------------------------------------------------------------------------- /p02.py: -------------------------------------------------------------------------------- 1 | from common import * 2 | 3 | def draw(**kwargs): 4 | imgsz=np.array([2*1024]*2) 5 | box_tile=boxN(imgsz//8, 8) 6 | chk_tile=checkerboard(imgsz//8, imgsz//16) 7 | im=imtile(chk_tile^box_tile,imgsz) 8 | return im 9 | 10 | if __name__ == '__main__': 11 | im=draw() 12 | imshow(im) 13 | imsave(im,'p02.png') 14 | -------------------------------------------------------------------------------- /p03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fferri/geometric_patterns/f10c0781c30282ca517e00ba1ffe887d73f602b1/p03.png -------------------------------------------------------------------------------- /p03.py: -------------------------------------------------------------------------------- 1 | from common import * 2 | 3 | def spiral(shape,nbands=16,twist=0.1): 4 | r,a=meshgrid_polar(shape) 5 | return np.sin(np.log(1+r)*twist+a*nbands)>0 6 | 7 | def draw(**kwargs): 8 | imgsz=(1024,1024) 9 | s1,s2=(spiral(imgsz,16,16*i) for i in (1,-1)) 10 | return s1^s2 11 | 12 | if __name__ == '__main__': 13 | im=draw() 14 | imshow(im) 15 | imsave(im,'p03.png') 16 | -------------------------------------------------------------------------------- /p04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fferri/geometric_patterns/f10c0781c30282ca517e00ba1ffe887d73f602b1/p04.png -------------------------------------------------------------------------------- /p04.py: -------------------------------------------------------------------------------- 1 | from common import * 2 | 3 | def square_spiral(shape,num_cycles): 4 | w,b=np.ones(shape,dtype=np.uint8),np.zeros(shape,dtype=np.uint8) 5 | im=np.hstack((b,w)) 6 | z=[[w,b],[w,b],[b,w],[b,w]] 7 | for i in range(2,3+num_cycles): 8 | j=(i-2)%4 9 | if i<2+num_cycles: 10 | im=np.vstack((np.kron([1]*i,z[j][0]),im,np.kron([1]*i,z[j][1]))) 11 | else: 12 | im=np.vstack((np.kron([1]*i,z[j][0]),im)) 13 | im=im.T 14 | return im 15 | 16 | def draw(**kwargs): 17 | s=square_spiral((4,4),10) 18 | s1=1-s.T[...,::-1] 19 | s2=s1[::-1,::-1] 20 | s2=s2[...,5:] 21 | q=np.hstack((s1,s2)) 22 | q[-4:,...]=1 23 | q=np.vstack((q,q[...,::-1])) 24 | im=imtile(q,np.array(q.shape)*10) 25 | return im 26 | 27 | if __name__ == '__main__': 28 | im=draw() 29 | imshow(im) 30 | imsave(im,'p04.png') 31 | -------------------------------------------------------------------------------- /p05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fferri/geometric_patterns/f10c0781c30282ca517e00ba1ffe887d73f602b1/p05.png -------------------------------------------------------------------------------- /p05.py: -------------------------------------------------------------------------------- 1 | from common import * 2 | 3 | # 8-sides checkerboard 4 | 5 | def draw(**kwargs): 6 | imgsz=np.array([2*1024]*2) 7 | def radial_warp(i,j): 8 | cx,cy=imgsz/2 9 | a,r=np.arctan2(i-cy,j-cx),np.sqrt((i-cy)**2+(j-cx)**2) 10 | a=np.fmod(a,math.pi*2) 11 | return cx+np.cos(a)*r,cy+np.sin(a)*r 12 | im=checkerboard(imgsz, imgsz//16) 13 | im=imwarp(im,radial_warp,cycle) 14 | return im 15 | 16 | if __name__ == '__main__': 17 | im=draw() 18 | imshow(im) 19 | imsave(im,'p05.png') 20 | -------------------------------------------------------------------------------- /p06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fferri/geometric_patterns/f10c0781c30282ca517e00ba1ffe887d73f602b1/p06.png -------------------------------------------------------------------------------- /p06.py: -------------------------------------------------------------------------------- 1 | from common import * 2 | 3 | # 6-sides checkerboard 4 | 5 | def draw(**kwargs): 6 | imgsz=np.array([2*1024]*2) 7 | def radial_warp(i,j): 8 | cx,cy=imgsz/2 9 | a,r=np.arctan2(i-cy,j-cx),np.sqrt((i-cy)**2+(j-cx)**2) 10 | a=a*6/4 11 | return cx+np.cos(a)*r,cy+np.sin(a)*r 12 | im=checkerboard(imgsz, imgsz//16) 13 | im=imwarp(im,radial_warp,cycle) 14 | return im 15 | 16 | if __name__ == '__main__': 17 | im=draw() 18 | imshow(im) 19 | imsave(im,'p06.png') 20 | -------------------------------------------------------------------------------- /p07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fferri/geometric_patterns/f10c0781c30282ca517e00ba1ffe887d73f602b1/p07.png -------------------------------------------------------------------------------- /p07.py: -------------------------------------------------------------------------------- 1 | from common import * 2 | 3 | # 2-sides checkerboard 4 | 5 | def draw(**kwargs): 6 | imgsz=np.array([2*1024]*2) 7 | def radial_warp(i,j): 8 | cx,cy=imgsz/2 9 | a,r=np.arctan2(i-cy,j-cx),np.sqrt((i-cy)**2+(j-cx)**2) 10 | a=a*2/4 11 | return cx+np.cos(a)*r,cy+np.sin(a)*r 12 | im=checkerboard(imgsz, imgsz//16) 13 | im=imwarp(im,radial_warp,cycle) 14 | return im 15 | 16 | if __name__ == '__main__': 17 | im=draw() 18 | imshow(im) 19 | imsave(im,'p07.png') 20 | -------------------------------------------------------------------------------- /p08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fferri/geometric_patterns/f10c0781c30282ca517e00ba1ffe887d73f602b1/p08.png -------------------------------------------------------------------------------- /p08.py: -------------------------------------------------------------------------------- 1 | from common import * 2 | 3 | # 2-sides checkerboard 4 | 5 | 6 | def draw(**kwargs): 7 | imgsz=np.array([2*1024]*2) 8 | def radial_warp(i,j): 9 | cx,cy=imgsz/2 10 | a,r=np.arctan2(i-cy,j-cx),np.sqrt((i-cy)**2+(j-cx)**2) 11 | r=r*(1+0.4*np.sin(0.006*r)) 12 | a=a*6/4 13 | return cx+np.cos(a)*r,cy+np.sin(a)*r 14 | im=checkerboard(imgsz, imgsz//16) 15 | im=imwarp(im,radial_warp,cycle) 16 | return im 17 | 18 | if __name__ == '__main__': 19 | im=draw() 20 | imshow(im) 21 | imsave(im,'p08.png') 22 | -------------------------------------------------------------------------------- /p09.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fferri/geometric_patterns/f10c0781c30282ca517e00ba1ffe887d73f602b1/p09.png -------------------------------------------------------------------------------- /p09.py: -------------------------------------------------------------------------------- 1 | from common import * 2 | 3 | # 2-sides checkerboard 4 | 5 | def draw(**kwargs): 6 | imgsz=np.array([2*1024]*2) 7 | def radial_warp(i,j): 8 | cx,cy=imgsz/2 9 | a,r=np.arctan2(i-cy,j-cx),np.sqrt((i-cy)**2+(j-cx)**2) 10 | r=r*(1+0.1*np.sin(0.008*r)) 11 | a=a*6/4 12 | return cx+np.cos(a)*r,cy+np.sin(a)*r 13 | im=checkerboard(imgsz, imgsz//16)^imtile(boxN(imgsz//8,4),imgsz) 14 | im2=checkerboard(imgsz, imgsz//16)^imtile(boxN(imgsz//16,4),imgsz) 15 | im[512:1536,512:1536]=im2[512:1536,512:1536] 16 | im=imwarp(im,radial_warp,cycle) 17 | return im 18 | 19 | if __name__ == '__main__': 20 | im=draw() 21 | imshow(im) 22 | imsave(im,'p09.png') 23 | -------------------------------------------------------------------------------- /p10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fferri/geometric_patterns/f10c0781c30282ca517e00ba1ffe887d73f602b1/p10.png -------------------------------------------------------------------------------- /p10.py: -------------------------------------------------------------------------------- 1 | from common import * 2 | 3 | def draw(**kwargs): 4 | s=256 5 | im=checkerboard(s,s//2) 6 | for i in range(6): 7 | s//=2 8 | c=checkerboard(s,s//2) 9 | ch=imtile(c,(c.shape[0],im.shape[1])) 10 | cv=imtile(c,(im.shape[0]+2*c.shape[0],c.shape[1])) 11 | im=np.hstack((cv,np.vstack((ch,im,ch)),cv)) 12 | imgsz=np.uint(im.shape) 13 | def radial_warp(i,j): 14 | cx,cy=imgsz/2 15 | a,r=np.arctan2(i-cy,j-cx),np.sqrt((i-cy)**2+(j-cx)**2) 16 | a=a*6/4 17 | r=r*np.sin(1000/(1+r)) 18 | return cx+np.cos(a)*r,cy+np.sin(a)*r 19 | im=imwarp(im,radial_warp,cycle) 20 | return im 21 | 22 | if __name__ == '__main__': 23 | im=draw() 24 | imshow(im) 25 | imsave(im,'p10.png') 26 | -------------------------------------------------------------------------------- /p11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fferri/geometric_patterns/f10c0781c30282ca517e00ba1ffe887d73f602b1/p11.png -------------------------------------------------------------------------------- /p11.py: -------------------------------------------------------------------------------- 1 | from common import * 2 | from functools import reduce 3 | 4 | def draw(**kwargs): 5 | s=np.array((4096,4096)) 6 | y,x=meshgrid_euclidean(s) 7 | pts=[] 8 | for (r,n,o) in ((0,1,0),(0.2,3,math.pi/6),(0.4,6,0)): 9 | for a in range(n): 10 | pts.append([getattr(math,f)(o+a*math.pi*2/n)*r+0.5 for f in ['cos','sin']]) 11 | r=[np.sqrt((x-p[1]*s[1])**2+(y-p[0]*s[0])**2) for p in pts] 12 | r=reduce(np.minimum, r[1:], r[0]) 13 | im=np.sin(r*math.pi/40)>0 14 | return im 15 | 16 | if __name__ == '__main__': 17 | im=draw() 18 | imshow(im) 19 | imsave(im,'p11.png') 20 | -------------------------------------------------------------------------------- /p12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fferri/geometric_patterns/f10c0781c30282ca517e00ba1ffe887d73f602b1/p12.png -------------------------------------------------------------------------------- /p12.py: -------------------------------------------------------------------------------- 1 | from common import * 2 | 3 | def draw(**kwargs): 4 | w,h=2048,2048 5 | x,y=np.meshgrid(range(w),range(h)) 6 | r=np.sqrt((x-w/2)**2+(y-h/2)**2) 7 | a=np.arctan2(x-w/2,y-h/2) 8 | im1=np.sin(np.log(1+r)*math.pi*16)>0 9 | im2=np.sin(4*math.pi*np.cos(a*8+8*np.log(1+r)))>0 10 | return im1^im2 11 | 12 | if __name__ == '__main__': 13 | im=draw() 14 | imshow(im) 15 | imsave(im,'p12.png') 16 | -------------------------------------------------------------------------------- /p13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fferri/geometric_patterns/f10c0781c30282ca517e00ba1ffe887d73f602b1/p13.png -------------------------------------------------------------------------------- /p13.py: -------------------------------------------------------------------------------- 1 | from common import * 2 | 3 | # hyperbolic coords checkerboard 4 | 5 | def draw(**kwargs): 6 | w,h=2048,2048 7 | u,v=meshgrid_hyperbolic((w,h)) 8 | u=np.uint(u*10)%2 9 | v=np.uint(v//300)%2 10 | im=u^v 11 | im=np.hstack((im[...,::-1],im)) 12 | im=np.vstack((im[::-1,...],im)) 13 | return im 14 | 15 | if __name__ == '__main__': 16 | im=draw() 17 | imshow(im) 18 | imsave(im,'p13.png') 19 | -------------------------------------------------------------------------------- /p14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fferri/geometric_patterns/f10c0781c30282ca517e00ba1ffe887d73f602b1/p14.png -------------------------------------------------------------------------------- /p14.py: -------------------------------------------------------------------------------- 1 | from common import * 2 | 3 | def draw(**kwargs): 4 | imgsz=np.array([2*1024]*2) 5 | r,a=meshgrid_polar(imgsz,dist=distance.L2) 6 | r=np.uint(7*np.log(1+r)) 7 | im=np.zeros(imgsz,dtype=np.uint8) 8 | for i in range(8,17): 9 | c,d,k=i*3,(i+1)*3,2**(i-6) 10 | im|=(np.uint(np.floor(a*k/math.pi/2))%2)*(r>=c)*(r0)) 7 | 8 | # fast box blur: 9 | for n in (65,): 10 | for axis in range(2): 11 | im=sum(np.roll(im,i,axis) for i in range(-n//2,(n+1)//2)) 12 | 13 | im=imnormalize(im)>(0.25+r/imgsz[0])*255 14 | return im 15 | 16 | if __name__ == '__main__': 17 | im=draw() 18 | imshow(im) 19 | imsave(im,'p24.png') 20 | -------------------------------------------------------------------------------- /p25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fferri/geometric_patterns/f10c0781c30282ca517e00ba1ffe887d73f602b1/p25.png -------------------------------------------------------------------------------- /p25.py: -------------------------------------------------------------------------------- 1 | from common import * 2 | 3 | # floor tiles 4 | 5 | def draw(**kwargs): 6 | imgsz=(2048,)*2 7 | x,y=meshgrid_euclidean(imgsz) 8 | s=lambda x: np.sin(math.pi*x) 9 | b=0.5 10 | h=s(y*8/imgsz[0])+b*s(x*8/imgsz[1]) 11 | v=s(1+x*8/imgsz[0])+b*s(y*8/imgsz[1]) 12 | im=(h>0)^(v>0) 13 | return im 14 | 15 | if __name__ == '__main__': 16 | im=draw() 17 | imshow(im) 18 | imsave(im,'p25.png') 19 | -------------------------------------------------------------------------------- /video.py: -------------------------------------------------------------------------------- 1 | from common import * 2 | 3 | im = None 4 | 5 | def radial_warp(t,imgsz): 6 | def f(i,j): 7 | cx,cy=imgsz/2 8 | a,r=np.arctan2(i-cy,j-cx),np.sqrt((i-cy)**2+(j-cx)**2) 9 | r=r*(1+(0.1*math.sin(t*0.01)+0.1)*np.sin((0.008+math.sin(0.0007*t)*0.01)*r+t*0.005637)) 10 | a=a*6/4 11 | return cx+np.cos(a)*r,cy+np.sin(a)*r 12 | return f 13 | 14 | def draw(t=0, **kwargs): 15 | imgsz=np.array([2*1024]*2) 16 | global im 17 | if im is None: 18 | im=checkerboard(imgsz, imgsz//16)^imtile(boxN(imgsz//8,4),imgsz) 19 | im2=checkerboard(imgsz, imgsz//16)^imtile(boxN(imgsz//16,4),imgsz) 20 | im[512:1536,512:1536]=im2[512:1536,512:1536] 21 | return imwarp(im,radial_warp(t,imgsz),cycle) 22 | 23 | if __name__ == '__main__': 24 | for t in range(10000): 25 | print('rendering frame %08d...'%t) 26 | imsave(draw(t),'video%08d.png'%t) 27 | 28 | -------------------------------------------------------------------------------- /video2.py: -------------------------------------------------------------------------------- 1 | from common import * 2 | from functools import reduce 3 | 4 | def draw(t=0, **kwargs): 5 | s=np.array((2048,2048)) 6 | y,x=meshgrid_euclidean(s) 7 | pts=[] 8 | for (r,n,o) in ((0,1,0),(0.2+0.12*math.sin(t*0.03),3,0.001*t+math.pi/6),(0.4+0.3*math.sin(0.34+0.0174*t),6,math.sin(0.4+0.0042*t)*math.pi)): 9 | for a in range(n): 10 | pts.append([getattr(math,f)(o+a*math.pi*2/n)*r+0.5 for f in ['cos','sin']]) 11 | r=[np.sqrt((x-p[1]*s[1])**2+(y-p[0]*s[0])**2) for p in pts] 12 | r=reduce(np.minimum, r[1:], r[0]) 13 | im=np.sin(r*math.pi/(40+10*math.sin(0.43586+0.006342*t)))>0 14 | return im 15 | 16 | if __name__ == '__main__': 17 | for t in range(10000): 18 | print('rendering frame %08d...'%t) 19 | im=draw(t) 20 | imsave(im,'video2-%08d.png'%t) 21 | -------------------------------------------------------------------------------- /video3.py: -------------------------------------------------------------------------------- 1 | from common import * 2 | 3 | def radial_warp(t): 4 | def f(i,j): 5 | cx,cy=imgsz/2 6 | a,r=np.arctan2(i-cy,j-cx),norm.L2(i-cy,j-cx) 7 | r+=(1+np.cos(0.1*t+r*math.pi*8/imgsz[0]))*imgsz[0]/(1+10*np.log(1+r)) 8 | a+=r*t/imgsz[0]/1000 9 | return cx+np.cos(a)*r,cy+np.sin(a)*r 10 | return f 11 | 12 | imgsz=np.array([2*1024]*2) 13 | im=checkerboard(imgsz, imgsz//16) 14 | 15 | def draw(t=0, **kwargs): 16 | return imwarp(im,radial_warp(t),cycle) 17 | 18 | if __name__ == '__main__': 19 | for t in range(4000): 20 | print('rendering frame %08d...'%t) 21 | im1=draw(t) 22 | imsave(im1,'video3-%08d.png'%t) 23 | -------------------------------------------------------------------------------- /video4.py: -------------------------------------------------------------------------------- 1 | from common import * 2 | 3 | def kaleidoscope(x,y): 4 | def f(i,j): 5 | cy,cx=imgsz[0]*y,imgsz[1]*x 6 | r,a=np.sqrt((i-cy)**2+(j-cx)**2),np.arctan2(i-cy,j-cx) 7 | l=math.pi*2/4 8 | a=np.abs(np.fmod(1.5*(2*math.pi+a),l)-l/2)*2+math.sin(x+y+math.sin(x+4*y)) 9 | return cy+r*np.sin(a),cx+r*np.cos(a) 10 | return f 11 | 12 | def spiral(shape,nbands=16,twist=0.1): 13 | r,a=meshgrid_polar(shape) 14 | return np.sin(np.log(1+10*(1+np.sin(r*0.002)))*twist+a*nbands)+1/(1.2+0.0007*r) 15 | 16 | imgsz=(1024,1024) 17 | s1,s2=(spiral(imgsz,6,32*i) for i in (-1,1)) 18 | im=s1*s2 19 | 20 | def draw(t=0, **kwargs): 21 | a=t*0.008 22 | r=0.2+0.15*math.sin(a*3) 23 | im2=imwarp(im,kaleidoscope(0.5+r*math.sin(a),0.5+r*math.cos(a))) 24 | im2=apply_colormap(im2,colormap.jet) 25 | return im2 26 | 27 | if __name__ == '__main__': 28 | for t in range(4000): 29 | print('rendering frame %08d...'%t) 30 | im2=draw(t) 31 | imsave(im2,'video4-%08d.png'%t) 32 | 33 | -------------------------------------------------------------------------------- /video5.py: -------------------------------------------------------------------------------- 1 | from common import * 2 | 3 | # plasma effect 4 | 5 | imgsz=(1280,800) 6 | y,x=meshgrid_euclidean(imgsz) 7 | 8 | def draw(t=0, **kwargs): 9 | cx1,cy1=x-imgsz[1]*0.5*(1+math.cos(t*0.01)),y-imgsz[0]*0.5*(1+math.sin(t*0.01)) 10 | v1=np.sin(np.sqrt(cx1**2+cy1**2)/imgsz[0]*12+t*0.0354837) 11 | v2=np.sin(9*(1+0.4*math.sin(t*0.04566))*x/imgsz[1]*math.sin(t*0.01)+7*(1+0.6*math.cos(t*0.0463))*y/imgsz[0]*math.cos(t*0.00784)+t*0.0295528) 12 | v3=np.sin(0.546427+np.sqrt(cx1**2+cy1**2)/imgsz[0]*6+t*0.0156737) 13 | v4=np.sin(0.4635+3*(1+0.5*math.sin(t*0.06566))*x/imgsz[1]*math.sin(t*0.01)+5*(1+0.6*math.cos(t*0.0463))*y/imgsz[0]*math.cos(t*0.00784)+t*0.0195528) 14 | im=v1*(0.7+0.6*math.sin(t*0.04526))+v2*(0.8+0.7*math.cos(t*0.05))+v3+v4 15 | im=apply_colormap(im,colormap.jet) 16 | return im 17 | 18 | if __name__ == '__main__': 19 | for t in range(8000): 20 | print('rendering frame %08d...'%t) 21 | im=draw(t) 22 | imsave(im,'video5-%08d.png'%t) 23 | -------------------------------------------------------------------------------- /video6.py: -------------------------------------------------------------------------------- 1 | from common import * 2 | 3 | imgsz=np.array((512,512)) 4 | c=[imgsz*(0.5,d) for d in (0.25,0.75)] 5 | (r1,a1),(r2,a2)=(meshgrid_polar(imgsz,c[i]) for i in range(2)) 6 | cmap=colormap.rainbow() 7 | 8 | def draw(t=0, **kwargs): 9 | k=0.03*t/250 10 | im=np.minimum(r1*np.sin(k*r2),r2*np.sin(k*r1)) 11 | cmap=np.roll(cmap,-1,axis=0) 12 | im=apply_colormap(im,cmap) 13 | return im 14 | 15 | if __name__ == '__main__': 16 | for t in range(2153): 17 | print('rendering frame %08d...'%t) 18 | im=draw(t) 19 | imsave(im,'video6-%08d.png'%t) 20 | -------------------------------------------------------------------------------- /video7.py: -------------------------------------------------------------------------------- 1 | from common import * 2 | 3 | imgsz=(1280,800) 4 | r,a=meshgrid_polar(imgsz) 5 | 6 | def draw(t=0, **kwargs): 7 | h=t*math.pi*2/100 8 | im=3*(3*np.log(1+r))+5*np.sin(a*8+16*np.log(1+r)) 9 | im=apply_colormap(im,colormap.rainbow2(h)) 10 | return im 11 | 12 | if __name__ == '__main__': 13 | for t in range(100): 14 | print('rendering frame %08d...'%t) 15 | im=draw(t) 16 | imsave(im,'video7-%08d.png'%t) 17 | -------------------------------------------------------------------------------- /video8.py: -------------------------------------------------------------------------------- 1 | from common import * 2 | 3 | imgsz=(2048,)*2 4 | r,a=meshgrid_polar(imgsz) 5 | im=np.float32(np.uint8(np.log(1+r)*4%2)^np.uint8(np.sin(a*16)>0)) 6 | 7 | def draw(t=0, **kwargs): 8 | im2=im 9 | # fast box blur: 10 | for n in (1+2*t,): 11 | for axis in range(2): 12 | im2=sum(np.roll(im2,i,axis) for i in range(-n//2,(n+1)//2)) 13 | im2=imnormalize(im2)>(0.25+r/imgsz[0])*255 14 | return im2 15 | 16 | if __name__ == '__main__': 17 | for t in range(1000): 18 | print('rendering frame %08d...'%t) 19 | im2=draw(t) 20 | imsave(im2,'video8-%08d.png'%t) 21 | -------------------------------------------------------------------------------- /video8a.py: -------------------------------------------------------------------------------- 1 | from common import * 2 | 3 | imgsz=(2048,)*2 4 | r,a=meshgrid_polar(imgsz) 5 | im=np.float32(np.uint8(np.log(1+r)*4%2)^np.uint8(np.sin(a*16)>0)) 6 | 7 | im2=im 8 | 9 | def draw(t=0, **kwargs): 10 | # fast box blur: 11 | for n in (19,): 12 | for axis in range(2): 13 | im2=sum(np.roll(im2,i,axis) for i in range(-n//2,(n+1)//2)) 14 | im2/=n*n 15 | im3=imnormalize(im2)>(0.25+r/imgsz[0])*255 16 | return im3 17 | 18 | if __name__ == '__main__': 19 | for t in range(1000): 20 | print('rendering frame %08d...'%frame) 21 | im3=draw(t) 22 | imsave(im3,'video8a-%08d.png'%frame) 23 | -------------------------------------------------------------------------------- /video8b.py: -------------------------------------------------------------------------------- 1 | from common import * 2 | 3 | imgsz=(2048,)*2 4 | r,a=meshgrid_polar(imgsz) 5 | 6 | def draw(t=0, **kwargs): 7 | discs=np.uint8(np.log(1+r)*4%2) 8 | bands=np.uint8(np.sin(a*16+0.1*t-np.log(1+r)*t)>0) 9 | im2=np.float32(discs^bands) 10 | # fast box blur: 11 | n=1+2*int(t*5) 12 | for axis in range(2): 13 | im2=sum(np.roll(im2,i,axis) for i in range(-n//2,(n+1)//2)) 14 | im2/=n*n 15 | im3=imnormalize(im2)-(0.25+r/imgsz[0])*255 16 | im3=apply_colormap(im3,colormap.hot) 17 | return im3 18 | 19 | if __name__ == '__main__': 20 | for t in range(1000): 21 | print('rendering frame %08d...'%t) 22 | im3=draw(0.05*t) 23 | imsave(im3,'video8b-%08d.png'%frame) 24 | -------------------------------------------------------------------------------- /video9.py: -------------------------------------------------------------------------------- 1 | from common import * 2 | 3 | # floor tiles metamorphosis 4 | 5 | imgsz=(2048,)*2 6 | x,y=meshgrid_euclidean(imgsz) 7 | c=lambda x: np.cos(math.pi*x) 8 | f=8./imgsz[0] 9 | 10 | def draw(t=0, nf=250, **kwargs): 11 | b=min(1.,max(0.,1.3*abs(math.fmod(2*t/125.,2)-1))) 12 | q=int(t>=nf*0.25 and t<=nf*0.75) 13 | im=(c(y*f)+b*c(x*f)>0)^(c(q+x*f)+b*c(y*f)>0)^q 14 | return im 15 | 16 | if __name__ == '__main__': 17 | for t in range(nf): 18 | print('rendering frame %08d...'%t) 19 | im=draw(t) 20 | imsave(im,'video9-%08d.png'%t) 21 | --------------------------------------------------------------------------------