Squiggles
Posted: February 7th, 2020, 10:06 pm
The idea is to start with tiles based on matching points like parentheses. The points along the bottom of each tile are connected with circular arcs. E.g., ()(()) would connect positions 0 and 1, 2 and 5, 3 and 4. When you place these tiles together, you may get one or more closed curves. Which ones pair up to form a simple closed curve without holes?
I think this is a versatile tile matching rule that is easy to check visually. That is, you can immediately see if the result is a simple closed curve without holes. The Python script to create it using imagemagick.
I think this is a versatile tile matching rule that is easy to check visually. That is, you can immediately see if the result is a simple closed curve without holes. The Python script to create it using imagemagick.
Code: Select all
def parens(n):
for res in parens_recur(n * 2, 0, []):
yield res
def parens_recur(n, depth, sofar):
if n == len(sofar):
yield "".join(sofar)
else:
if len(sofar) + depth < n:
sofar.append("(")
for res in parens_recur(n, depth + 1, sofar):
yield res
sofar.pop()
if depth > 0:
sofar.append(")")
for res in parens_recur(n, depth - 1, sofar):
yield res
sofar.pop()
def links(paren_string):
stack = []
for i in range(len(paren_string)):
if paren_string[i] == '(':
stack.append(i)
else:
yield (stack.pop(), i)
def perm(links):
links = list(links)
ix = [0] * len(links) * 2
for i, j in links:
ix[i], ix[j] = j, i
return ix
def rotate(links):
return [len(links) - i -1 for i in links[::-1]]
def loops(uplinks, downlinks):
seen = [False for x in uplinks]
loops = []
for i in range(len(uplinks)):
if not seen[i]:
loop = []
j = i
while not seen[j]:
seen[j] = True
loop.append(j)
j = uplinks[j]
seen[j] = True
loop.append(j)
j = downlinks[j]
loops.append(loop)
return loops
def to_curve(loops, scale, x, y):
yield "-fill black -stroke none -draw 'fill-rule evenodd translate %5.3f %5.3f path \"" % (x, y)
for loop in loops:
n = len(loop)
yield 'M %5.3f 0' % (loop[0] * scale)
for i in range(n):
x1 = loop[i] * scale
x2 = loop[(i + 1) % n] * scale
sweep = ((0 if x1 > x2 else 1) + i) % 2
radius = abs(x2 - x1) * 0.5
yield 'A %5.3f %5.3f 0 0 %5.3f %5.3f 0' % (radius, radius, sweep, x2)
yield ' Z'
yield "\"'"
def to_upper(links, scale, x, y):
edges = [i + (1 - (i % 2) * 2) for i in range(len(links))]
yield "-fill black -stroke none -draw 'fill-rule evenodd translate %5.3f %5.3f path \"" % (x, y)
for loop in loops(links, edges):
n = len(loop)
yield 'M %5.3f 0' % (loop[0] * scale)
for i in range(n):
x1 = loop[i] * scale
x2 = loop[(i + 1) % n] * scale
sweep = ((0 if x1 > x2 else 1) + i) % 2
radius = abs(x2 - x1) * 0.5
if i % 2 == 0:
yield 'A %5.3f %5.3f 0 0 %5.3f %5.3f 0' % (radius, radius, sweep, x2)
else:
yield 'L %5.3f 0' % x2
yield ' Z'
yield "\"'"
n = 5
allix = [perm(links(s)) for s in parens(n)]
'''
for i in range(len(allix)):
for j in range(len(allix)):
print("*" if 1 == len(loops(allix[i], rotate(allix[j]))) else ".", end=' ')
print()
'''
def nextpos(x, y):
x += 85
if x > 1700:
x = 50
y += 90
return x, y
x = 50
y = 100
lines = ["magick -size 1800x1800 canvas:none "]
for i in range(len(allix)):
lines.extend(to_upper(allix[i], 7, x, y))
lines.append("-fill none -stroke blue -draw 'translate %5.3f %5.3f rectangle %5.3f 0 %5.3f %5.3f'" %
(x, y, -5, (2 * n - 1) * 8 + 5, -n * 8 - 5))
x, y = nextpos(x, y)
x = 50
y += 90
for i in range(len(allix)):
for j in range(len(allix)):
path = loops(allix[i], rotate(allix[j]))
if len(path) == 1:
lines.extend(to_curve(path, 7, x, y))
lines.append("-fill none -stroke blue -draw 'translate %5.3f %5.3f rectangle %5.3f 0 %5.3f %5.3f'" %
(x, y, -5, (2 * n - 1) * 8 + 5, -n * 8 - 5))
lines.append("-fill none -stroke blue -draw 'translate %5.3f %5.3f rectangle %5.3f 0 %5.3f %5.3f'" %
(x, y, -5, (2 * n - 1) * 8 + 5, n * 8 - 5))
x, y = nextpos(x, y)
lines.append("-fill red -draw 'translate %5.3f %5.3f rectangle 20 -3 26 3'" % (x, y))
x, y = nextpos(x, y)
lines.append("t.png")
print(" \\\n".join(lines))