Truchet Tiles are decorated squares which can tile a plane to create a pattern. For example, the four tiles:
can be placed at random orientations to yield a non-repeating pattern:
Similarly the two types of tile consisting of two quarter-circles in diagonally-opposite vertices of a square:
can be used to create the following pleasing pattern:
The code used to create these images consists of three files: truchet.py
defines a base class, Truchet
, which is derived in two further classes, TruchetTriangles
in truchet_triangles.py
and TruchetArcs
in truchet_arcs.py
. We use a Python decorator to wrap the SVG style definitions in the necessary SVG <defs>
block. There are many tutorials dealing with Python decorators on the internet, so no detailed explanation will be given here: the function defs_decorator
takes the function svg_styles
defined in each derived class and returns a modified version of that function in which the opening and closing SVG tags are written around the CSS styles.
truchet.py
:
class Truchet:
"""Base class for a Truchet tiling."""
def __init__(self, width, height, s):
"""Initialize the class with image size and tile size, s."""
self.width, self.height = width, height
self.s = s
self.nx, self.ny = int(width // s), int(height // s)
self.fo = None
def preamble(self):
"""The usual SVG preamble, including the image size."""
print('<?xml version="1.0" encoding="utf-8"?>\n'
'<svg xmlns="http://www.w3.org/2000/svg"\n' + ' '*5 +
'xmlns:xlink="http://www.w3.org/1999/xlink" width="{}" height="{}" >'
.format(self.width, self.height), file=self.fo)
def defs_decorator(func):
"""For convenience, wrap the CSS styles with the needed SVG tags."""
def wrapper(self):
print("""
<defs>
<style type="text/css"><![CDATA[""", file=self.fo)
func(self)
print("""]]></style>
</defs>""", file=self.fo)
return wrapper
def svg_shape(self, *args, **kwargs):
"""Override this function in the derived class."""
def make_svg(self, filename, *args, **kwargs):
"""Create the tiling image as an SVG file with name filename.
Custom arguments are passed to the derived class's svg_shape method.
"""
self.fo = open(filename, 'w')
self.preamble()
self.svg_styles()
self.svg_shape(*args, **kwargs)
print('</svg>', file=self.fo)
truchet_triangles.py
:
import random
from truchet import Truchet
class TruchetTriangles(Truchet):
"""A class for creating a Truchet tiling of triangles."""
def __init__(self, width, height, s, colour):
super(TruchetTriangles, self).__init__(width, height, s)
self.colour = colour
@Truchet.defs_decorator
def svg_styles(self):
print('.tri {{ stroke: none; fill: {}; }}'.format(self.colour),
file=self.fo)
def svg_shape(self, rule=None):
"""A Truchet figure based on triangles.
The four triangle orientations to choose from in each square are:
xx x. xx .x
.x xx x. xx
"""
if rule is None:
rule = lambda ix, iy: random.randint(0,4)
def triangle_path(A, B, C):
"""Output a triangular path with vertices at A, B, C."""
print('<path d="M{},{} L{},{} L{},{}z" class="tri"/>'.format(
*A, *B, *C), file=self.fo)
for ix in range(self.nx):
for iy in range(self.ny):
x0, y0 = ix*self.s, iy*self.s
x1, y1 = (ix+1)*self.s, (iy+1)*self.s
p = rule(ix, iy)
if p == 0:
triangle_path((x0, y0), (x1, y0), (x1, y1))
elif p == 1:
triangle_path((x0, y0), (x0, y1), (x1, y1))
elif p == 2:
triangle_path((x0, y0), (x1, y0), (x0, y1))
else:
triangle_path((x1, y0), (x1, y1), (x0, y1))
if __name__ == '__main__':
truchet = TruchetTriangles(600, 400, 20, colour='#882ecf')
truchet.make_svg('triangles.svg')
truchet_arcs.py
:
import random
from truchet import Truchet
class TruchetArcs(Truchet):
"""A class for creating a Truchet tiling of arcs."""
def __init__(self, width, height, s, colour):
super(TruchetArcs, self).__init__(width, height, s)
self.colour = colour
@Truchet.defs_decorator
def svg_styles(self):
print('.arc {{ stroke: {}; stroke-width: 3px; fill: none; }}'
.format(self.colour), file=self.fo)
def svg_shape(self, r=None, rule=None):
"""A Truchet figure based on interlinking circular arcs."""
def arc_path(A, B, r):
"""Semicircular arc path from A=(x0,y0) to B=(x1,y1), radius r."""
print('<path d="M{},{} A{},{} 0 0 1 {} {}" class="arc"/>'.format(
*A, r, r, *B), file=self.fo)
if rule is None:
rule = lambda ix, iy: random.randint(0,1)
if not r:
r = self.s / 2
for ix in range(self.nx):
for iy in range(self.ny):
p = rule(ix, iy)
x0, y0 = ix*self.s, iy*self.s
A, B = (x0,y0 + r), (x0 + r,y0 + self.s),
C, D = (x0 + self.s,y0 + r), (x0 + r,y0)
if p:
arc_path(A, B, r)
arc_path(C, D, r)
else:
arc_path(D, A, r)
arc_path(B, C, r)
if __name__ == '__main__':
truchet = TruchetArcs(600, 400, 20, colour='#2e88cf')
truchet.make_svg('arcs.svg')
Comments
Comments are pre-moderated. Please be patient and your comment will appear soon.
BCM 5 years, 1 month ago
How would you do this in freeglut code? Like Codeblocks?
Link | ReplyNew Comment