spb/internetpressure
Public
TypeScript 36.3%
Python 31.8%
Go 18%
JavaScript 9.8%
Shell 1.9%
SQL 1.4%
CSS 0.5%
1#!/usr/bin/env python32"""Generate the InternetPressure.io brand assets from one geometric definition.34The mark is a *pressure dial*: a 240° ring cut into the seven pressure bands (calm → extreme, the palette used across5the site), a needle and a hub. It works in colour on dark, and in a single colour for tiny sizes.67Outputs (run from the repo root, needs rsvg-convert + Pillow):8 apps/web/public/brand/mark.svg the dial, 64×64, transparent9 apps/web/public/brand/mark-mono.svg single-colour variant (currentColor)10 apps/web/public/logo.svg dial + wordmark, 360×6411 apps/web/src/app/icon.svg favicon (dial on a dark rounded tile)12 apps/web/src/app/icon.png 512×51213 apps/web/src/app/apple-icon.png 180×18014 apps/web/src/app/favicon.ico 16/32/4815 apps/web/public/brand/og-wallpaper.png 1200×630 static share image (used when the live one is unavailable)16 apps/web/src/components/chrome/Logo.tsx React component with the same geometry17"""1819from __future__ import annotations2021import math22import pathlib23import subprocess2425ROOT = pathlib.Path(__file__).resolve().parents[1]26WEB = ROOT / "apps/web"27PALETTE = ["#4CC9F0", "#7FB77E", "#E9C46A", "#F4A261", "#E76F51", "#D62828", "#F72585"]28INK = "#E6EDF3"29INK2 = "#8B98A5"30BG = "#070A0F"31TILE = "#0C1117"32ACCENT = "#5B8DEF"3334CX = CY = 32.035R = 24.0 # ring radius36W = 6.5 # ring stroke width37START, SWEEP = 150.0, 240.0 # gauge from 150° (bottom-left) clockwise 240° to 30° (bottom-right)38GAP = 3.0 # degrees between bands39NEEDLE_DEG = START + SWEEP * 0.43 # needle at ~ "elevated" — the state the gauge is built to show40NEEDLE_LEN = 17.0414243def pt(deg: float, r: float) -> tuple[float, float]:44 a = math.radians(deg)45 return CX + r * math.cos(a), CY + r * math.sin(a)464748def arc(d0: float, d1: float, r: float) -> str:49 x0, y0 = pt(d0, r)50 x1, y1 = pt(d1, r)51 large = 1 if (d1 - d0) > 180 else 052 return f"M{x0:.2f} {y0:.2f} A{r} {r} 0 {large} 1 {x1:.2f} {y1:.2f}"535455def bands() -> list[tuple[str, str]]:56 n = len(PALETTE)57 span = (SWEEP - GAP * (n - 1)) / n58 out = []59 for i, col in enumerate(PALETTE):60 d0 = START + i * (span + GAP)61 out.append((arc(d0, d0 + span, R), col))62 return out636465def needle() -> tuple[tuple[float, float], tuple[float, float]]:66 return (CX, CY), pt(NEEDLE_DEG, NEEDLE_LEN)676869def dial_svg_inner(*, mono: str | None = None, needle_color: str = INK, hub: str = INK) -> str:70 parts = []71 for d, col in bands():72 parts.append(f'<path d="{d}" stroke="{mono or col}" stroke-width="{W}" stroke-linecap="round" fill="none"/>')73 (x0, y0), (x1, y1) = needle()74 # needle: a tapered line — drawn as a polygon so it stays crisp at 16 px75 a = math.radians(NEEDLE_DEG)76 px, py = -math.sin(a) * 2.2, math.cos(a) * 2.277 parts.append(f'<polygon points="{x0+px:.2f},{y0+py:.2f} {x0-px:.2f},{y0-py:.2f} {x1:.2f},{y1:.2f}" fill="{mono or needle_color}"/>')78 parts.append(f'<circle cx="{CX}" cy="{CY}" r="4.2" fill="{mono or hub}"/>')79 parts.append(f'<circle cx="{CX}" cy="{CY}" r="1.6" fill="{BG if not mono else "none"}"/>')80 return "\n ".join(parts)818283def mark_svg(mono: bool = False) -> str:84 inner = dial_svg_inner(mono="currentColor" if mono else None)85 return f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64" role="img" aria-label="InternetPressure.io">\n <!-- InternetPressure.io — the pressure dial: seven bands (calm → extreme), needle, hub -->\n {inner}\n</svg>\n'868788def icon_svg() -> str:89 inner = dial_svg_inner()90 return (f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64">\n'91 f' <rect width="64" height="64" rx="14" fill="{TILE}"/>\n {inner}\n</svg>\n')929394def logo_svg() -> str:95 inner = dial_svg_inner()96 return (f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 360 64" width="360" height="64" role="img" aria-label="InternetPressure.io">\n'97 f' <g>{inner}</g>\n'98 f' <text x="76" y="41" font-family="Geist, Inter, -apple-system, Segoe UI, Helvetica, Arial, sans-serif" font-size="28" font-weight="600" letter-spacing="-0.6" fill="{INK}">InternetPressure<tspan fill="{INK2}" font-weight="500">.io</tspan></text>\n'99 f'</svg>\n')100101102def og_svg() -> str:103 """Static wallpaper: a big dial ghosted on the right, the wordmark and tagline on the left."""104 scale = 8.5105 inner = dial_svg_inner()106 return f'''<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 630" width="1200" height="630">107 <defs>108 <radialGradient id="glow" cx="72%" cy="50%" r="45%"><stop offset="0" stop-color="#E9C46A" stop-opacity="0.16"/><stop offset="1" stop-color="{BG}" stop-opacity="0"/></radialGradient>109 <pattern id="grid" width="24" height="24" patternUnits="userSpaceOnUse"><path d="M24 0H0V24" fill="none" stroke="#1B2430" stroke-width="0.6"/></pattern>110 </defs>111 <rect width="1200" height="630" fill="{BG}"/>112 <rect width="1200" height="630" fill="url(#grid)" opacity="0.55"/>113 <rect width="1200" height="630" fill="url(#glow)"/>114 <g transform="translate({1200 - 32*scale - 40:.0f} {(630 - 64*scale)/2 + 30:.0f}) scale({scale})" opacity="0.92">{inner}</g>115 <g transform="translate(72 84) scale(1.0)">{inner}</g>116 <text x="152" y="128" font-family="Geist, Inter, -apple-system, Segoe UI, Helvetica, Arial, sans-serif" font-size="46" font-weight="600" letter-spacing="-1" fill="{INK}">InternetPressure<tspan fill="{INK2}" font-weight="500">.io</tspan></text>117 <text x="72" y="300" font-family="Geist, Inter, -apple-system, Segoe UI, Helvetica, Arial, sans-serif" font-size="22" letter-spacing="6" fill="{INK2}">GLOBAL INTERNET PRESSURE</text>118 <text x="72" y="392" font-family="Geist, Inter, -apple-system, Segoe UI, Helvetica, Arial, sans-serif" font-size="64" font-weight="600" letter-spacing="-1.5" fill="{INK}">The real-time pressure gauge</text>119 <text x="72" y="466" font-family="Geist, Inter, -apple-system, Segoe UI, Helvetica, Arial, sans-serif" font-size="64" font-weight="600" letter-spacing="-1.5" fill="{INK}">for the Internet.</text>120 <text x="72" y="560" font-family="Geist, Inter, -apple-system, Segoe UI, Helvetica, Arial, sans-serif" font-size="20" fill="{INK2}">Independent probes on three continents · RIPE RIS Live · one explainable 0–100 index · www.internetpressure.io</text>121</svg>122'''123124125def logo_tsx() -> str:126 paths = "\n".join(f' <path d="{d}" stroke={{mono ? color : "{col}"}} strokeWidth={{{W}}} strokeLinecap="round" fill="none" />' for d, col in bands())127 (x0, y0), (x1, y1) = needle()128 a = math.radians(NEEDLE_DEG)129 px, py = -math.sin(a) * 2.2, math.cos(a) * 2.2130 return f'''/** InternetPressure.io mark — the pressure dial (generated by scripts/brand.py; same geometry as public/brand/mark.svg).131 * Seven bands = the pressure palette calm → extreme, a needle and a hub. `mono` renders everything in `color`. */132export function Logo({{ size = 22, className, color = 'currentColor', mono = false }}: {{ size?: number; className?: string; color?: string; mono?: boolean }}) {{133 return (134 <svg viewBox="0 0 64 64" width={{size}} height={{size}} className={{className}} aria-hidden="true">135{paths}136 <polygon points="{x0+px:.2f},{y0+py:.2f} {x0-px:.2f},{y0-py:.2f} {x1:.2f},{y1:.2f}" fill={{mono ? color : '{INK}'}} />137 <circle cx="{CX}" cy="{CY}" r="4.2" fill={{mono ? color : '{INK}'}} />138 {{!mono && <circle cx="{CX}" cy="{CY}" r="1.6" fill="{BG}" />}}139 </svg>140 );141}}142'''143144145def main() -> None:146 brand = WEB / "public/brand"147 brand.mkdir(parents=True, exist_ok=True)148 (brand / "mark.svg").write_text(mark_svg())149 (brand / "mark-mono.svg").write_text(mark_svg(mono=True))150 (WEB / "public/logo.svg").write_text(logo_svg())151 (WEB / "src/app/icon.svg").write_text(icon_svg())152 (WEB / "src/components/chrome/Logo.tsx").write_text(logo_tsx())153 og = brand / "og-wallpaper.svg"154 og.write_text(og_svg())155156 def raster(src: pathlib.Path, dst: pathlib.Path, w: int, h: int | None = None) -> None:157 subprocess.run(["rsvg-convert", "-w", str(w), "-h", str(h or w), str(src), "-o", str(dst)], check=True)158159 raster(WEB / "src/app/icon.svg", WEB / "src/app/icon.png", 512)160 raster(WEB / "src/app/icon.svg", WEB / "src/app/apple-icon.png", 180)161 raster(og, brand / "og-wallpaper.png", 1200, 630)162 raster(brand / "mark.svg", brand / "mark-256.png", 256)163 from PIL import Image164165 base = Image.open(WEB / "src/app/icon.png").convert("RGBA")166 base.save(WEB / "src/app/favicon.ico", sizes=[(16, 16), (32, 32), (48, 48)])167 og.unlink() # keep only the PNG wallpaper (the SVG relies on system fonts)168 print("brand assets written")169170171if __name__ == "__main__":172 main()173