import math import os def squircle(size=64, n=5.0, steps=90): r = size/2 pts=[] for i in range(steps*4): t = 2*math.pi*i/(steps*4) c, s = math.cos(t), math.sin(t) pts.append((r*math.copysign(abs(c)**(2/n), c)+r, r*math.copysign(abs(s)**(2/n), s)+r)) return "M"+" L".join(f"{x:.2f} {y:.2f}" for x,y in pts)+"Z" def norm(v): l=math.hypot(*v); return (v[0]/l, v[1]/l) def lam(top=(19.2,10.0), bot=(44.8,54.0), foot=(21.0,54.0), join_t=0.50, w=6.8): """A lambda drawn the way a type designer builds one: two straight strokes of one weight, the long one running top-left to bottom-right and the short one leaving it part way down and landing at the bottom-left, every end cut square to the baseline, and no part of one stroke poking out of the other.""" (tx,ty),(bx,by)=top,bot # --- the long stroke: a parallelogram cut horizontally at both ends d=norm((bx-tx,by-ty)); phi_h = w/abs(d[1]) # horizontal thickness def long_x(y): return tx+(bx-tx)*(y-ty)/(by-ty) long_poly=[(tx-phi_h/2,ty),(tx+phi_h/2,ty),(bx+phi_h/2,by),(bx-phi_h/2,by)] # --- the short stroke starts on the long one's centre line jy=ty+(by-ty)*join_t; jx=long_x(jy) sd=norm((foot[0]-jx,foot[1]-jy)) # direction of the short stroke sh = w/abs(sd[1]) # its horizontal thickness nrm=(-sd[1], sd[0]) # unit normal # the two edges of the short stroke, as lines through (jx,jy)+-nrm*w/2 def edge_pt(sign, y): ox,oy=jx+sign*nrm[0]*w/2, jy+sign*nrm[1]*w/2 t=(y-oy)/sd[1]; return (ox+sd[0]*t, y) # its top end follows the long stroke's own direction, through the join point, # so it ends inside the long stroke instead of sticking out of it def on_long_dir(sign): ox,oy=jx+sign*nrm[0]*w/2, jy+sign*nrm[1]*w/2 # intersect edge line (o + sd*t) with the line (j + d*s) a=sd[0]*(-d[1]) - sd[1]*(-d[0]); t=((jx-ox)*(-d[1]) - (jy-oy)*(-d[0]))/a return (ox+sd[0]*t, oy+sd[1]*t) p1,p2=on_long_dir(+1),on_long_dir(-1) q1,q2=edge_pt(+1,foot[1]),edge_pt(-1,foot[1]) short_poly=[p1,p2,q2,q1] return long_poly, short_poly def path(*polys): return " ".join("M"+" L".join(f"{x:.2f} {y:.2f}" for x,y in p)+"Z" for p in polys) BLUE="#1B2BD0" def svg(bg=BLUE, fg="#FFFFFF", size=64): l,s=lam() return (f'' f'') # --------------------------------------------------------------------------- # Writes services/webmail/src/app/icon.svg and the SVGs the PNG step renders. # Run: python3 scripts/brand/mark.py then node scripts/brand/icons.mjs # --------------------------------------------------------------------------- HERE = os.path.dirname(os.path.abspath(__file__)) WEBMAIL = os.path.join(HERE, "..", "..", "services", "webmail") def write_all(): l, s = lam() glyph = path(l, s) shift = 'transform="translate(-0.9 0)"' # centres the glyph, not its bounding box tile = f'' head = '' icon = f'{head}\n \n {tile}\n \n\n' full = f'{head}' safe = f'{head}' open(os.path.join(WEBMAIL, "src", "app", "icon.svg"), "w").write(icon) for name, text in (("logo.svg", icon), ("logo-full.svg", full), ("logo-safe.svg", safe)): open(os.path.join(HERE, name), "w").write(text) print("glyph:", glyph) if __name__ == "__main__": write_all()