- Logo/favicon ridisegnati come theta maiuscola: cerchio (anello) con 'H' centrata, clearance verificata (non tocca l'anello); ring ispessito per la favicon; centratura ottica verificata. - Rimosso il badge 'Self-hosted Git' dal hero della homepage (era esteticamente di troppo) - CSS e markup. - Aggiornati generate_theta.py, README, home.tmpl; caricati sul VPS in staging (md5 locali == remoti); tarball ricostruito. - Salvataggio iniziale dell'intero admin pack in git.
140 lines
5.5 KiB
Python
140 lines
5.5 KiB
Python
#!/usr/bin/env python3
|
|
# Logo/favicon Gitea: THETA MAIUSCOLA = cerchio (anello) con una "H" al centro.
|
|
# La H non tocca l'anello (clearance sicura). Niente coda.
|
|
# Emette sia gli SVG (logo/favicon) che i PNG (logo/favicon/apple-touch-icon)
|
|
# partendo dalla STESSA geometria, così SVG e PNG combaciano.
|
|
import os
|
|
from PIL import Image, ImageDraw
|
|
|
|
OUT = "custom/public/assets/img"
|
|
|
|
# --- geometria normalizzata (frazioni del lato quadrato) ---
|
|
INSET = 0.04 # bordo del riquadro arrotondato
|
|
RAD = 0.22 # raggio angoli del riquadro
|
|
CX, CY = 0.5, 0.5 # centro (simmetrico, nessuna coda)
|
|
RO = 0.41 # raggio esterno anello
|
|
RING = 0.05 # spessore anello -> raggio interno RI = RO - RING
|
|
# H centrale
|
|
HH = 0.26 # mezza altezza della H (stelo da CY-HH a CY+HH)
|
|
SPAC = 0.145 # distanza orizzontale dal centro agli steli
|
|
STEM = 0.05 # spessore steli
|
|
CROSS = 0.042 # spessore barra orizzontale
|
|
|
|
C1, C2 = (124, 58, 237), (14, 165, 233) # viola -> azzurro
|
|
|
|
RI = RO - RING
|
|
|
|
|
|
def lerp(a, b, t):
|
|
return int(a + (b - a) * t)
|
|
|
|
|
|
def grad_color(t):
|
|
return (lerp(C1[0], C2[0], t), lerp(C1[1], C2[1], t), lerp(C1[2], C2[2], t))
|
|
|
|
|
|
def theta_geo(size):
|
|
cx, cy = CX * size, CY * size
|
|
ro, ri = RO * size, RI * size
|
|
stem = STEM * size
|
|
cross = CROSS * size
|
|
# steli: centro x = cx ± SPAC
|
|
left_x0 = (CX - SPAC - STEM / 2) * size
|
|
left_x1 = (CX - SPAC + STEM / 2) * size
|
|
right_x0 = (CX + SPAC - STEM / 2) * size
|
|
right_x1 = (CX + SPAC + STEM / 2) * size
|
|
st_y0, st_y1 = (CY - HH) * size, (CY + HH) * size
|
|
# barra: attraversa tutta la larghezza della H, centrata su CY
|
|
cb_y0, cb_y1 = CY * size - cross / 2, CY * size + cross / 2
|
|
return dict(cx=cx, cy=cy, ro=ro, ri=ri,
|
|
left_x0=left_x0, left_x1=left_x1,
|
|
right_x0=right_x0, right_x1=right_x1,
|
|
st_y0=st_y0, st_y1=st_y1,
|
|
cb_x0=left_x0, cb_x1=right_x1, cb_y0=cb_y0, cb_y1=cb_y1,
|
|
stem=stem, cross=cross)
|
|
|
|
|
|
def circle_path(r, cx, cy, size):
|
|
lx, rx = cx - r, cx + r
|
|
return (f"M {lx:.2f} {cy:.2f} A {r:.2f} {r:.2f} 0 1 0 {rx:.2f} {cy:.2f} "
|
|
f"A {r:.2f} {r:.2f} 0 1 0 {lx:.2f} {cy:.2f} Z")
|
|
|
|
|
|
def svg_theta(size):
|
|
g = theta_geo(size)
|
|
inset, rad = INSET * size, RAD * size
|
|
w = size
|
|
|
|
def e(x):
|
|
return f"{x:.2f}"
|
|
|
|
outer = circle_path(g["ro"], g["cx"], g["cy"], size)
|
|
inner = circle_path(g["ri"], g["cx"], g["cy"], size)
|
|
|
|
lines = []
|
|
lines.append(f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {size} {size}" width="{size}" height="{size}">')
|
|
lines.append(' <defs><linearGradient id="zg" x1="0" y1="0" x2="0" y2="1">'
|
|
'<stop offset="0" stop-color="#7c3aed"/><stop offset="1" stop-color="#0ea5e9"/></linearGradient></defs>')
|
|
lines.append(f' <rect x="{e(inset)}" y="{e(inset)}" width="{e(w-2*inset)}" height="{e(w-2*inset)}" rx="{e(rad)}" fill="url(#zg)"/>')
|
|
# anello (theta maiuscola = cerchio)
|
|
lines.append(f' <path fill-rule="evenodd" fill="#fff" d="{outer}{inner}"/>')
|
|
# H centrale (steli + barra)
|
|
lines.append(f' <rect x="{e(g["left_x0"])}" y="{e(g["st_y0"])}" width="{e(g["left_x1"]-g["left_x0"])}" height="{e(g["st_y1"]-g["st_y0"])}" fill="#fff"/>')
|
|
lines.append(f' <rect x="{e(g["right_x0"])}" y="{e(g["st_y0"])}" width="{e(g["right_x1"]-g["right_x0"])}" height="{e(g["st_y1"]-g["st_y0"])}" fill="#fff"/>')
|
|
lines.append(f' <rect x="{e(g["cb_x0"])}" y="{e(g["cb_y0"])}" width="{e(g["cb_x1"]-g["cb_x0"])}" height="{e(g["cb_y1"]-g["cb_y0"])}" fill="#fff"/>')
|
|
lines.append('</svg>')
|
|
return "\n".join(lines) + "\n"
|
|
|
|
|
|
def draw_theta_png(size, apple=False):
|
|
g = theta_geo(size)
|
|
img = Image.new("RGBA", (size, size), (0, 0, 0, 0))
|
|
inset, rad = INSET * size, RAD * size
|
|
|
|
# sfondo: riquadro arrotondato con gradiente verticale
|
|
sq = Image.new("L", (size, size), 0)
|
|
ImageDraw.Draw(sq).rounded_rectangle(
|
|
[inset, inset, size - inset, size - inset], radius=rad, fill=255)
|
|
grad = Image.new("RGBA", (size, size), (0, 0, 0, 0))
|
|
gd = ImageDraw.Draw(grad)
|
|
for i in range(size * 2):
|
|
t = i / (size * 2 - 1)
|
|
gd.line([(i, 0), (i, size)], fill=grad_color(t))
|
|
grad.putalpha(sq)
|
|
img.alpha_composite(grad)
|
|
|
|
# theta+H come maschera bianca
|
|
mask = Image.new("L", (size, size), 0)
|
|
md = ImageDraw.Draw(mask)
|
|
md.ellipse([g["cx"] - g["ro"], g["cy"] - g["ro"],
|
|
g["cx"] + g["ro"], g["cy"] + g["ro"]], fill=255)
|
|
md.ellipse([g["cx"] - g["ri"], g["cy"] - g["ri"],
|
|
g["cx"] + g["ri"], g["cy"] + g["ri"]], fill=0)
|
|
md.rectangle([g["left_x0"], g["st_y0"], g["left_x1"], g["st_y1"]], fill=255)
|
|
md.rectangle([g["right_x0"], g["st_y0"], g["right_x1"], g["st_y1"]], fill=255)
|
|
md.rectangle([g["cb_x0"], g["cb_y0"], g["cb_x1"], g["cb_y1"]], fill=255)
|
|
|
|
white = Image.new("RGBA", (size, size), (255, 255, 255, 255))
|
|
white.putalpha(mask)
|
|
img.alpha_composite(white)
|
|
|
|
if apple:
|
|
base = Image.new("RGBA", img.size, (255, 255, 255, 255))
|
|
base.alpha_composite(img)
|
|
return base.convert("RGB")
|
|
return img
|
|
|
|
|
|
def make_assets():
|
|
os.makedirs(OUT, exist_ok=True)
|
|
open(f"{OUT}/logo.svg", "w").write(svg_theta(64))
|
|
open(f"{OUT}/favicon.svg", "w").write(svg_theta(64))
|
|
draw_theta_png(220).save(f"{OUT}/logo.png")
|
|
draw_theta_png(64).save(f"{OUT}/favicon.png")
|
|
draw_theta_png(180, apple=True).save(f"{OUT}/apple-touch-icon.png")
|
|
print("theta (capital: circle + H) assets written to", OUT)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
make_assets()
|