shapes.py

In source/At fed4d1cCopy the commit ID

fed4d1cHistory

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)

Files