shapes.py
In source/At fed4d1cCopy the commit ID
Preview
Code5.1 KiB"""Individual solids and symbol treatments from the canonical logo geometry."""
from copy import deepcopy
from pathlib import Path
import subprocess
import xml.etree.ElementTree as E
from .logo import render, NS, element
from .design import UNO_BLUE
SHAPES=['sphere','pyramid','cube','cylinder']
BOUNDS={'sphere':(-382.5,-244.25799,-147.5,-9.25799),'pyramid':(-232.5,-375,82.5,-66.25),'cube':(-82.5,-235.34,187.5,65),'cylinder':(162.5,-382.5,372.5,-45)}
STYLES=['Gradient','Flat faces','Uno tonal','Blue outline','White reverse','Color on blue']
COLORS={'pyramid':['#ff6552','#ef3c3c'],'cylinder':['#13b856','#36d76c','#79e58c'],'sphere':['#0067ed','#319eff'],'cube':['#ffed72','#edbc17','#f6d52e']}
def make(shape,style):
original=E.fromstring(render(False))
root=E.Element(NS+'svg',{'width':'800','height':'800','viewBox':'0 0 320 320','role':'img','aria-labelledby':'title desc'})
element(root,'title',id='title').text='KIT '+(shape.title() if shape else 'shape composition')+' / '+STYLES[style]
element(root,'desc',id='desc').text='Geometric KIT artwork without lettering. '+STYLES[style]+'.'
defs=deepcopy(original.find(NS+'defs'));root.append(defs)
if style>=4:element(root,'rect',width=320,height=320,rx=32,fill=UNO_BLUE)
scene=original.find(NS+'svg')
if shape:
x0,y0,x1,y1=BOUNDS[shape]
outer=element(root,'g',transform=f'translate({160-(x0+x1)*.325} {263-y1*.65}) scale(.65)')
outer.append(deepcopy(next(g for g in scene if g.get('aria-label').lower()==shape)))
else:
outer=element(root,'svg',x=20,y=34,width=280,height=252,viewBox='-430 -430 860 560')
for g in scene:outer.append(deepcopy(g))
for g in list(outer):
name=g.get('aria-label').lower()
faces=[e for e in g if e.tag in [NS+'path',NS+'circle']]
for i,e in enumerate(faces):
if style==1:
e.set('fill',COLORS[name][i%len(COLORS[name])]);e.attrib.pop('stroke',None)
elif style==2:
tones={'pyramid':['#319eff','#0052c0'],'cylinder':['#0052c0','#0067ed','#8acbff'],'sphere':['#0067ed','#8acbff'],'cube':['#8acbff','#0052c0','#319eff']}
e.set('fill',tones[name][i%len(tones[name])]);e.attrib.pop('stroke',None)
elif style in [3,4]:
e.set('fill','white');e.set('stroke',UNO_BLUE);e.set('stroke-width','3.5');e.set('stroke-linejoin','round')
if style==5:
e.set('stroke','white');e.set('stroke-width','1.4');e.set('stroke-linejoin','round')
# Keep only gradients referenced by this asset.
used=' '.join(v for e in outer.iter() for v in e.attrib.values())
for d in list(defs):
if 'url(#'+d.get('id','')+')' not in used:defs.remove(d)
return root
def embed(sheet,asset,x,y,w,h,prefix):
nested=deepcopy(asset);nested.attrib.update(x=str(x),y=str(y),width=str(w),height=str(h))
ids={e.get('id'):prefix+e.get('id') for e in nested.iter() if e.get('id')}
for e in nested.iter():
for k,v in list(e.attrib.items()):
if k=='id':v=ids[v]
elif k=='aria-labelledby':v=' '.join(ids.get(a,a) for a in v.split())
else:
for old,new in ids.items():v=v.replace('url(#'+old+')','url(#'+new+')')
e.set(k,v)
sheet.append(nested)
def write(root,name,svg):
art=root/'art'/'shapes';previews=root/'previews'/'shapes'
art.mkdir(parents=True,exist_ok=True);previews.mkdir(parents=True,exist_ok=True)
path=art/(name+'.svg');path.write_text(E.tostring(svg,encoding='unicode'))
subprocess.run(['rsvg-convert','-o',str(previews/(name+'.png')),str(path)],check=True)
def board(width,height,title,subtitle):
s=E.Element(NS+'svg',{'width':str(width),'height':str(height),'viewBox':f'0 0 {width} {height}'})
element(s,'rect',width=width,height=height,fill='#f3f5f8')
element(s,'text',x=40,y=52,fill=UNO_BLUE,font_family='Helvetica, Arial, sans-serif',font_size=28,font_weight=700).text=title
element(s,'text',x=40,y=84,fill='#536174',font_family='Helvetica, Arial, sans-serif',font_size=16).text=subtitle
return s
def build(root):
sheet=board(1680,1430,'KIT / Shape library','Four solids · six treatments · shared geometry')
for j,style in enumerate(STYLES):
x=40+j*268
element(sheet,'text',x=x+8,y=134,fill=UNO_BLUE,font_family='Helvetica, Arial, sans-serif',font_size=17,font_weight=700).text=f'{j+1:02} / {style}'
for i,name in enumerate(SHAPES):
y=155+i*310
element(sheet,'rect',x=x,y=y,width=252,height=292,rx=14,fill='white')
svg=make(name,j);write(root,f'{name}-{j+1:02}',svg)
embed(sheet,svg,x+16,y+6,220,248,f'{name}-{j}-')
element(sheet,'text',x=x+18,y=y+273,fill='#536174',font_family='Helvetica, Arial, sans-serif',font_size=16).text=name.title()
write(root,'shape-library',sheet)
groups=board(1680,1230,'KIT / Symbols without lettering','The original composition in six complementary treatments')
for j,style in enumerate(STYLES):
x=40+(j%3)*548;y=118+(j//3)*548
element(groups,'rect',x=x,y=y,width=512,height=512,rx=16,fill='white')
svg=make(None,j);write(root,f'composition-{j+1:02}',svg)
embed(groups,svg,x+38,y+14,436,436,f'group-{j}-')
element(groups,'text',x=x+25,y=y+483,fill=UNO_BLUE,font_family='Helvetica, Arial, sans-serif',font_size=20,font_weight=700).text=f'{j+1:02} / {style}'
write(root,'symbol-library',groups)
print('Built 24 individual shapes, six compositions, and two comparison sheets.')
if __name__=='__main__':build(Path(__file__).resolve().parent.parent)