Skip to content

Some matplotlib/numpy version of the spectre script #1

@logari81

Description

@logari81

Thanks for this nice port to python, here there is a version of your code ported to numpy and using matplotlib for plotting

#!/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 = 4

IDENTITY = np.array([[1,0,0],
                     [0,1,0]], 'float32')
TILE_NAMES = ["Gamma", "Delta", "Theta", "Lambda", "Xi", "Pi", "Sigma", "Phi", "Psi"]

COLOR_MAP = {"Gamma":  np.array((211,  95,  95),'f')/255.,
             "Gamma1": np.array((200,  55,  55),'f')/255.,
             "Gamma2": np.array((222, 135, 135),'f')/255.,
             "Delta":  np.array((  0, 255, 255),'f')/255.,
             "Theta":  np.array((255, 170, 238),'f')/255.,
             "Lambda": np.array((128, 128, 128),'f')/255.,
             "Xi":     np.array((145,  95, 211),'f')/255.,
             "Pi":     np.array(( 95,  95, 211),'f')/255.,
             "Sigma":  np.array(( 85, 212,   0),'f')/255.,
             "Phi":    np.array((255, 179, 128),'f')/255.,
             "Psi":    np.array((255, 221,  85),'f')/255.}

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, label):
        self.label = label
        self.quad = SPECTRE_QUAD.copy()

    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.label]))

class MetaTile:
    def __init__(self, tiles=[], transformations=[], quad=SPECTRE_QUAD.copy()):
        self.tiles = tiles
        self.transformations = transformations
        self.quad = quad

    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 = {}
    tiles["Gamma"] = MetaTile(tiles=[Tile("Gamma1"),Tile("Gamma2")],
                                     transformations=[IDENTITY.copy(),trsf],
                                     quad=SPECTRE_QUAD.copy())
    for label in TILE_NAMES:
        if label != "Gamma":
            tiles[label] = Tile(label) 
    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["Delta"].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 label, substitutions in (("Gamma",  ("Pi",  "Delta", None,  "Theta", "Sigma", "Xi",  "Phi",    "Gamma")),
                                 ("Delta",  ("Xi",  "Delta", "Xi",  "Phi",   "Sigma", "Pi",  "Phi",    "Gamma")),
                                 ("Theta",  ("Psi", "Delta", "Pi",  "Phi",   "Sigma", "Pi",  "Phi",    "Gamma")),
                                 ("Lambda", ("Psi", "Delta", "Xi",  "Phi",   "Sigma", "Pi",  "Phi",    "Gamma")),
                                 ("Xi",     ("Psi", "Delta", "Pi",  "Phi",   "Sigma", "Psi", "Phi",    "Gamma")),
                                 ("Pi",     ("Psi", "Delta", "Xi",  "Phi",   "Sigma", "Psi", "Phi",    "Gamma")),
                                 ("Sigma",  ("Xi",  "Delta", "Xi",  "Phi",   "Sigma", "Pi",  "Lambda", "Gamma")),
                                 ("Phi",    ("Psi", "Delta", "Psi", "Phi",   "Sigma", "Pi",  "Phi",    "Gamma")),
                                 ("Psi",    ("Psi", "Delta", "Psi", "Phi",   "Sigma", "Psi", "Phi",    "Gamma"))):
        tiles[label] =\
            MetaTile(tiles=[input_tiles[subst] for subst in substitutions if subst],
                     transformations=[trsf for subst, trsf in zip(substitutions, transformations) if subst],
                     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["Delta"].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()

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions