assets: add GoSentry wordmark logo (clock-dial variant)
Recommended variant from gosentry-logo.html: Space Grotesk SemiBold with an amber "G", a clock-dial "o" (ring + hands), and petrol "Sentry". Ships self-contained SVGs (glyphs outlined to paths, no font dependency) in light/dark/mono plus transparent PNGs at 256-2048px, with a README and the gen_logo.py/raster.py generators for reproducibility. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Rasterize the GoSentry wordmark to PNG at several widths, reusing the
|
||||
same font outlines + dial geometry as gen_logo.py (no SVG rasterizer needed)."""
|
||||
import os, math
|
||||
import matplotlib
|
||||
matplotlib.use("Agg")
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.path import Path
|
||||
from matplotlib.patches import PathPatch, Circle
|
||||
from matplotlib.lines import Line2D
|
||||
from fontTools.ttLib import TTFont
|
||||
from fontTools.pens.basePen import BasePen
|
||||
from fontTools.pens.boundsPen import BoundsPen
|
||||
from fontTools.pens.transformPen import TransformPen
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
OUT = os.path.join(HERE, "out"); os.makedirs(OUT, exist_ok=True)
|
||||
AMBER, PETROL, WHITE = "#F7A80C", "#0A4A58", "#FFFFFF"
|
||||
LS = -30
|
||||
|
||||
f = TTFont(os.path.join(HERE, "SpaceGrotesk-600.ttf"))
|
||||
cmap, hmtx, gs = f.getBestCmap(), f["hmtx"], f.getGlyphSet()
|
||||
|
||||
class MplPen(BasePen):
|
||||
def __init__(self, glyphSet):
|
||||
super().__init__(glyphSet); self.v=[]; self.c=[]
|
||||
def _moveTo(self,p): self.v.append(p); self.c.append(Path.MOVETO)
|
||||
def _lineTo(self,p): self.v.append(p); self.c.append(Path.LINETO)
|
||||
def _curveToOne(self,p1,p2,p3):
|
||||
self.v += [p1,p2,p3]; self.c += [Path.CURVE4]*3
|
||||
def _qCurveToOne(self,p1,p2):
|
||||
self.v += [p1,p2]; self.c += [Path.CURVE3]*2
|
||||
def _closePath(self):
|
||||
self.v.append((0,0)); self.c.append(Path.CLOSEPOLY)
|
||||
|
||||
def glyph_mplpath(ch, dx):
|
||||
pen = MplPen(gs)
|
||||
gs[cmap[ord(ch)]].draw(TransformPen(pen,(1,0,0,1,dx,0)))
|
||||
return Path(pen.v, pen.c), hmtx[cmap[ord(ch)]][0]
|
||||
|
||||
# layout
|
||||
x=0.0
|
||||
paths_amber=[]; paths_petrol=[]
|
||||
p,adv = glyph_mplpath("G",x); paths_amber.append(p); x+=adv+LS
|
||||
og=cmap[ord("o")]; bp=BoundsPen(gs); gs[og].draw(bp)
|
||||
oxmin,oymin,oxmax,oymax=bp.bounds; o_adv=hmtx[og][0]
|
||||
cx=x+(oxmin+oxmax)/2; cy=(oymin+oymax)/2
|
||||
R=((oxmax-oxmin)+(oymax-oymin))/4
|
||||
x+=o_adv+LS
|
||||
for ch in "Sentry":
|
||||
p,adv=glyph_mplpath(ch,x); paths_petrol.append(p); x+=adv+LS
|
||||
x-=LS
|
||||
|
||||
SW=R*0.30; Rmid=R-SW/2; HW=R*0.21
|
||||
hour_len=R*0.54; min_len=R*0.46; ang=math.radians(62)
|
||||
mx=cx+min_len*math.sin(ang); my=cy+min_len*math.cos(ang); cap=R*0.14
|
||||
|
||||
# bounds
|
||||
allb=BoundsPen(gs); gs[cmap[ord('G')]].draw(allb)
|
||||
xx=hmtx[cmap[ord('G')]][0]+LS+o_adv+LS
|
||||
for ch in "Sentry":
|
||||
gs[cmap[ord(ch)]].draw(TransformPen(allb,(1,0,0,1,xx,0))); xx+=hmtx[cmap[ord(ch)]][0]+LS
|
||||
bx0,by0,bx1,by1=allb.bounds
|
||||
bx0=min(bx0,cx-R-SW/2); bx1=max(bx1,cx+R+SW/2)
|
||||
by0=min(by0,cy-R-SW/2); by1=max(by1,cy+R+SW/2)
|
||||
PAD=60
|
||||
X0,X1=bx0-PAD,bx1+PAD; Y0,Y1=by0-PAD,by1+PAD
|
||||
W=X1-X0; H=Y1-Y0
|
||||
|
||||
def render(path_png, width_px, sentry_color, bg=None, mono=False):
|
||||
dpi=100
|
||||
fw=width_px/dpi; fh=fw*H/W
|
||||
fig=plt.figure(figsize=(fw,fh),dpi=dpi)
|
||||
ax=fig.add_axes([0,0,1,1]); ax.set_xlim(X0,X1); ax.set_ylim(Y0,Y1)
|
||||
ax.set_aspect('equal'); ax.axis('off')
|
||||
if bg: fig.patch.set_facecolor(bg); ax.set_facecolor(bg)
|
||||
else: fig.patch.set_alpha(0)
|
||||
amberc = sentry_color if mono else AMBER
|
||||
for p in paths_amber: ax.add_patch(PathPatch(p,facecolor=amberc,edgecolor='none',antialiased=True))
|
||||
for p in paths_petrol: ax.add_patch(PathPatch(p,facecolor=sentry_color,edgecolor='none',antialiased=True))
|
||||
pt_per_unit = fw/W*72
|
||||
ax.add_patch(Circle((cx,cy),Rmid,fill=False,edgecolor=amberc,linewidth=SW*pt_per_unit))
|
||||
for (ex,ey) in [(cx,cy+hour_len),(mx,my)]:
|
||||
ax.add_line(Line2D([cx,ex],[cy,ey],color=amberc,linewidth=HW*pt_per_unit,
|
||||
solid_capstyle='round'))
|
||||
ax.add_patch(Circle((cx,cy),cap,facecolor=amberc,edgecolor='none'))
|
||||
fig.savefig(path_png,dpi=dpi,transparent=(bg is None))
|
||||
plt.close(fig)
|
||||
print("wrote",os.path.basename(path_png))
|
||||
|
||||
for w in (256,512,1024,2048):
|
||||
render(os.path.join(OUT,f"gosentry-logo-{w}.png"),w,PETROL)
|
||||
render(os.path.join(OUT,f"gosentry-logo-dark-{w}.png"),w,WHITE)
|
||||
render(os.path.join(OUT,"gosentry-logo-onlight-1024.png"),1024,PETROL,bg="#FFFFFF")
|
||||
render(os.path.join(OUT,"gosentry-logo-ondark-1024.png"),1024,WHITE,bg="#04262E")
|
||||
render(os.path.join(OUT,"gosentry-logo-mono-1024.png"),1024,PETROL,mono=True)
|
||||
print(f"aspect {W:.0f}x{H:.0f}")
|
||||
Reference in New Issue
Block a user