Open logari81 opened 1 year ago
and here a dictionary-free version of the same code
#!/usr/bin/python3
import numpy as np
from time import time
import matplotlib.pyplot as plt
PI = np.pi
# increase this number for larger tilings.
N_ITERATIONS = 0
IDENTITY = np.array([[1,0,0],
[0,1,0]], 'float32')
COLOR_MAP = (np.array((211, 95, 95),'f')/255., # 0: Gamma
np.array(( 0, 255, 255),'f')/255., # 1: Delta
np.array((255, 170, 238),'f')/255., # 2: Theta
np.array((128, 128, 128),'f')/255., # 3: Lambda
np.array((145, 95, 211),'f')/255., # 4: Xi
np.array(( 95, 95, 211),'f')/255., # 5: Pi
np.array(( 85, 212, 0),'f')/255., # 6: Sigma
np.array((255, 179, 128),'f')/255., # 7: Phi
np.array((255, 221, 85),'f')/255., # 8: Psi
np.array((200, 55, 55),'f')/255., # 9: Gamma1
np.array((222, 135, 135),'f')/255.) #10: Gamma2
SPECTRE_POINTS = np.array([(0.0, 0.0),
(1.0, 0.0),
(1.5, -np.sqrt(3)/2),
(1.5+np.sqrt(3)/2, 0.5-np.sqrt(3)/2),
(1.5+np.sqrt(3)/2, 1.5-np.sqrt(3)/2),
(2.5+np.sqrt(3)/2, 1.5-np.sqrt(3)/2),
(3+np.sqrt(3)/2, 1.5),
(3.0, 2.0),
(3-np.sqrt(3)/2, 1.5),
(2.5-np.sqrt(3)/2, 1.5+np.sqrt(3)/2),
(1.5-np.sqrt(3)/2, 1.5+np.sqrt(3)/2),
(0.5-np.sqrt(3)/2, 1.5+np.sqrt(3)/2),
(-np.sqrt(3)/2, 1.5),
(0.0, 1.0)], 'float32')
SPECTRE_QUAD = SPECTRE_POINTS[[3,5,7,11],:]
def mul(A, B):
AB = A.copy()
AB[:,:2] = A[:,:2].dot(B[:,:2])
AB[:,2] += A[:,:2].dot(B[:,2])
return AB
class Tile:
def __init__(self, tile_type):
self.type = tile_type
self.quad = SPECTRE_QUAD.copy()
def add_to_cluster(self, cluster, tile_transformation=IDENTITY.copy()):
cluster.append((self.type, tile_transformation))
def draw(self, polygons, tile_transformation=IDENTITY.copy()):
vertices = SPECTRE_POINTS.dot(tile_transformation[:,:2].T) + tile_transformation[:,2]
polygons.append((vertices, COLOR_MAP[self.type]))
class MetaTile:
def __init__(self, tiles=[], transformations=[], quad=SPECTRE_QUAD.copy()):
self.tiles = tiles
self.transformations = transformations
self.quad = quad
def add_to_cluster(self, cluster, transformation=IDENTITY.copy()):
for tile, trsf in zip(self.tiles, self.transformations):
tile.add_to_cluster(cluster, mul(transformation, trsf))
def draw(self, polygons, transformation=IDENTITY.copy()):
for tile, trsf in zip(self.tiles, self.transformations):
tile.draw(polygons, mul(transformation, trsf))
def buildSpectreBase():
ttrans = np.array([[1,0,SPECTRE_POINTS[8,0]],
[0,1,SPECTRE_POINTS[8,1]]])
trot = np.array([[np.cos(PI/6),-np.sin(PI/6),0.],
[np.sin(PI/6), np.cos(PI/6),0.]],'float32')
trsf = mul(ttrans, trot)
tiles = [MetaTile(tiles=[Tile(9),Tile(10)],
transformations=[IDENTITY.copy(),trsf],
quad=SPECTRE_QUAD.copy())]
tiles += [Tile(i) for i in range(1,9)]
return tiles
def buildSupertiles(input_tiles):
# First, use any of the nine-unit tiles in "tiles" to obtain a
# list of transformation matrices for placing tiles within supertiles.
quad = input_tiles[1].quad
transformations = [IDENTITY.copy()]
total_angle = 0
trot = IDENTITY.copy()
transformed_quad = quad
for _angle, _from, _to in (( PI/3, 3, 1),
( 0., 2, 0),
( PI/3, 3, 1),
( PI/3, 3, 1),
( 0., 2, 0),
( PI/3, 3, 1),
(-2*PI/3, 3, 3)):
if _angle != 0:
total_angle += _angle
trot = np.array([[1, 0,0],[0,1,0]])*np.cos(total_angle) \
+np.array([[0,-1,0],[1,0,0]])*np.sin(total_angle)
transformed_quad = quad.dot(trot[:,:2].T) # + trot[:,2]
last_trsf = transformations[-1]
ttrans = IDENTITY.copy()
ttrans[:,2] = last_trsf[:,:2].dot(quad[_from,:]) + last_trsf[:,2] \
-transformed_quad[_to,:]
transformations.append(mul(ttrans, trot))
R = np.array([[-1,0,0],[ 0,1,0]], 'float32')
transformations = [ mul(R, trsf) for trsf in transformations ]
# Now build the actual supertiles, labeling appropriately.
super_quad = quad[[2,1,2,1],:]
for i,itrsf in enumerate([6,5,3,0]):
trsf = transformations[itrsf]
super_quad[i,:] = trsf[:,:2].dot(super_quad[i,:]) + trsf[:,2]
tiles = []
for substitutions in ((5, 1, -1, 2, 6, 4, 7, 0),
(4, 1, 4, 7, 6, 5, 7, 0),
(8, 1, 5, 7, 6, 5, 7, 0),
(8, 1, 4, 7, 6, 5, 7, 0),
(8, 1, 5, 7, 6, 8, 7, 0),
(8, 1, 4, 7, 6, 8, 7, 0),
(4, 1, 4, 7, 6, 5, 3, 0),
(8, 1, 8, 7, 6, 5, 7, 0),
(8, 1, 8, 7, 6, 8, 7, 0)):
tiles.append(MetaTile(tiles=[input_tiles[subst] for subst in substitutions if subst >= 0],
transformations=[trsf for subst, trsf in zip(substitutions, transformations) if subst >= 0],
quad=super_quad))
return tiles
start = time()
tiles = buildSpectreBase()
for _ in range(N_ITERATIONS):
tiles = buildSupertiles(tiles)
time1 = time()-start
print(f"supertiling loop took {round(time1, 4)} seconds")
start = time()
polygons = []
tiles[1].draw(polygons)
time2 = time()-start
print(f"tile recursion loop took {round(time2, 4)} seconds, generated {len(polygons)} tiles")
plt.figure(figsize=(8, 8))
plt.axis('equal')
for pts,color in polygons:
plt.fill(pts[:,0],pts[:,1],facecolor=color)
plt.show()
Hello, thanks for your contribution! Feel free to open a pull request.
Hi. It's probably a good thing that we stopped keeping all the vertex coordinates of the spectre shape, as it allows us to draw larger shapes.
@logari81 Your version, which held the information for transform as a label in string format, was several times faster than the original version. but dictionary-free version is not good.
I also make Python code for more faster and more greater spectre tiles. and It made any ration of Spectre tile(a,b)
Prease see my Pullrequest.
The comparison results are shown below. (drowsvg is faster 30Times, file size shoter 5Times).
Prerequisites N_ITERATIONS = 5 (generates 34649 tiles)
matplotlib outputs matplotlib.pyplot SVG drawing took 21.1542 seconds matplotlib.pyplot: file save to spectre_tile10.0-10.0_5-34649pts.svg FILE-SIZE = 34,312,828 bytes. matplotlib.pyplot total processing time 21.3502 seconds, 616.1845 μs/tile
drowsvg outputs drowsvg: SVG drawing took 0.7256 seconds, generated 34649 tiles drowsvg: drawPolygon save to spectre_tile10.0-10.0_5-34649useRef.svg FILE-SIZE= 6,692,354 bytes. drowsvg: total processing time 0.7266 seconds, 20.9705 μs/tile
Thanks for this nice port to python, here there is a version of your code ported to numpy and using matplotlib for plotting