A few weeks ago a friend of mine from college met an artist named Michael Winkler (https://www.winklerwordart.com/) at a talk he was giving at some place. His art is (which I have some opinions on) consists of paintings that spell out words by connecting the lines between letters on an alphabet circle (see Figure 1). Michael Winkler and my friend spoke after the talk he gave and he mentioned that he had been trying to find/have someone write a program that would allow him to quickly mock up a word-circle. My friend thought I would bd2 make it.
The images are all SVGs (which can be programed as components using react). The components are all functional and use the hot-new-react-thing which are ‘state-hooks.’ I did have to start out by making an ipython notebook where I calculated all the relative points.
Figure 1: Circle and words (https://www.winklerwordart.com/images/Michael-Winkler-Art_Traced-Legend-2017_Website18.png)
Specifications for the circle:
E is at the top/north
Vowels (aeiou (not y)) are equidistant from each other
Consonants are spread equally between their bounding vowels
Figure 2: Paintings of words from Fig 1 (https://www.winklerwordart.com/images/Michael-Winkler-Art_TRACED_2017_Installed-NAP_400.png)
Regards,
Jesse
PS: check out that ‘https’ on the site (honestly it wasn't me but netlifly)!
import string
from math import pi
import pandas as pd
abcs = list(string.ascii_lowercase)
import numpy as maths # the british way of using numpy
import pygal
from IPython.display import SVG, display, HTML, display_svg
pie_chart = pygal.Pie()
pie_chart.title = 'equidistant ' + string.ascii_lowercase
abcs = list(string.ascii_lowercase)
for c in abcs: pie_chart.add(c, 360/26)
display(SVG(pie_chart.render(disable_xml_declaration=True)))
vowels = list('aeiou')
maths.linspace(0, 360, 5+1).tolist()
vowels_pie = pygal.Pie()
vowels_pie.title = 'vowels equidistant (aeiou)'
for c in vowels: vowels_pie.add(c, 360/5)
display(SVG(vowels_pie.render(disable_xml_declaration=True)))
alphabet_ix = {c:ix for ix, c in enumerate(abcs)}
_faux_vowels = [*vowels, 'a']
the_in_betweens = [maths.linspace(0, 360, 5+1).tolist()[i:i+2] for i in range(5)]
angles_from_north = []
for naybors, in_between in zip([_faux_vowels[i:i+2] for i in range(5)], the_in_betweens):
diff = alphabet_ix[naybors[1]] - alphabet_ix[naybors[0]]
diff = 26+diff if diff <0 else diff
angles_from_north.extend(maths.linspace(*in_between, diff+1).tolist()) # them angs
angles_from_north = list(sorted(set(angles_from_north)))
angles_diff = [angles_from_north[i+1]-angles_from_north[i] for i in range(len(angles_from_north)-1)]
print("here the angle diffs", angles_diff)
# abcde
# efghi
# ijklmno
# opqrstu
# uvwxyza
thingy_spacing = pygal.Pie() # really fantastic obj naming here (take note)
thingy_spacing.title = 'All letters with equidistant vowels (aeiou)'
for c, angle in zip(abcs, angles_diff): thingy_spacing.add(c, angle)
thingy_spacing.render()
display(SVG(thingy_spacing.render(disable_xml_declaration=True)))
maths.array(list(zip(abcs, angles_from_north)))
from svgwrite import Drawing
import numpy
import pandas as pd
xy=240
mid = xy//2
radius = ((xy/2) - 24)
# drawing = Drawing('afile.svg',height=xy, width=xy, profile='full')
arr = maths.array(list(zip(abcs[5:]+abcs[:5], angles_from_north)))
a = arr[:,1]
a = a.astype(float)
arr[:, 1] = a
arr = numpy.vstack([arr[:,0],a,( (a)*numpy.pi/180 )- (numpy.pi/2)]).T
arr = maths.array(list(zip(abcs, angles_from_north)))
dat = pd.DataFrame(arr)
dat.columns=['abc', 'deg']
dat.deg = (dat.deg.astype(numpy.float32) -72.0)%360
dat['rad'] = dat.deg*numpy.pi / 180 -(numpy.pi/2)
dat['xk'] = numpy.cos(dat['rad'])
dat['yk'] = numpy.sin(dat['rad'])
dat['dx'] = dat['xk']*radius
dat['x_txt'] = dat['xk']*radius*1.04+mid
dat['x'] = dat['dx'] + mid
dat['dy'] = dat['yk']*radius
dat['y'] = dat['dy'] + mid
dat['y_txt'] = dat['yk']*radius*1.04 + mid
dat['toops'] = dat[['abc','deg','x', 'y', 'x_txt', 'y_txt']].apply(tuple, axis=1)
dat.describe()
drawing = Drawing('base.svg',height=xy, width=xy)
drawing.viewbox(height=xy, width=xy)
drawing.add(drawing.rect((0, 0), (xy,xy), fill='black'))
drawing.add(drawing.rect((0+1, 0+1), (xy-(1*2),xy-(1*2)), fill='white'))
drawing.add(drawing.circle((xy/2, xy/2), r=radius+0.25))
drawing.add(drawing.circle((xy/2, xy/2), r=radius-0.25, fill='white'))
drawing.fit(scale='meet')
g = drawing.g(style=f"font-size:{int(numpy.sqrt(xy))};font-family:Helvetica;font-weight:lighter;stroke:black")
# time to make this cirlce
pts = {}
for abc, deg, x, y, x_txt, y_txt in dat['toops'].tolist():
pts[abc]=(x,y)
if abc in 'aeiou':
drawing.add(drawing.circle((x, y), r=2, fill='black'))
drawing.add(drawing.circle((x, y), r=1.5, fill='white'))
drawing.add(drawing.circle((x, y), r=1, fill='black'))
drawing.add(drawing.circle((x, y), r=0.5, fill='white'))
else:
drawing.add(drawing.circle((x, y), r=1.5, fill='black'))
drawing.add(drawing.circle((x, y), r=1, fill='white'))
g.add(drawing.text(str(abc).upper(), x=[x_txt], y=[y_txt],rotate=[deg]))
drawing.add(g)
drawing.save()
string = drawing.tostring()
display(SVG(string))
## This is lots of line making
# import os
# def mkline(cb):
# drawing = Drawing(os.path.join('lines',cb+'.svg'),height=xy, width=xy)
# drawing.viewbox(height=xy, width=xy)
# # drawing.add(drawing.rect((0, 0), (xy,xy), fill='black'))
# # drawing.add(drawing.rect((0+1, 0+1), (xy-(1*2),xy-(1*2)), fill='white'))
# aline = drawing.g(id=cb+'line')
# c1, c2 = cb
# pt1, pt2 = pts[c1], pts[c2]
# aline.add(drawing.line(start=pt1, end=pt2, stroke='black', stroke_width='1'))
# aline.add(drawing.circle(pt1, r=0.5, fill='black'))
# aline.add(drawing.circle(pt2, r=0.5, fill='black'))
# drawing.add(aline)
# drawing.save()
# mkline('fu') # super holistic testing happening right here
# from itertools import combinations
# combos = [mkline(''.join(cb)) for cb in combinations(abcs, r=2)]
letter_xys = {k: {'x':v[0], 'y':v[1]} for k, v in pts.items()}
from pprint import pprint
pprint(letter_xys)
# from pupy import sjson
# sjson('dato.jason_greenberg', letter_xys)