├── .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 |
--------------------------------------------------------------------------------