2026-11-04 16:02:05 +01:00 | | | """Tick Forge logos in the house style (dark field, faint price lines in an accent colour, bold white wordmark).
|
| | |
|
| | | Writes market/hero_1024.png (name + tagline) and market/logo_200.png, logo_140.png, logo_60.png (name only, large,
|
| | | so it stays readable at 60 px). The background lines are ONE price series repeated shifted in time and rescaled,
|
| | | which is what the tool does. Needs Pillow; uses DejaVu Sans if matplotlib is installed, else Arial.
|
| | | """
|
| | | import random
|
2026-11-04 07:30:30 +01:00 | | | from pathlib import Path
|
2026-11-04 16:02:05 +01:00 | | | from PIL import Image, ImageDraw, ImageFont
|
2026-11-04 07:30:30 +01:00 | | |
|
| | | OUT = Path(__file__).resolve().parent.parent / "market"
|
| | | OUT.mkdir(exist_ok=True)
|
2026-11-04 16:02:05 +01:00 | | | BG = (13, 17, 23)
|
| | | ACCENT = (232, 166, 54) # amber
|
| | | GREY = (150, 160, 178)
|
| | |
|
| | |
|
| | | def font(size, bold=True):
|
| | | try:
|
| | | import matplotlib
|
| | | d = Path(matplotlib.__file__).parent / "mpl-data" / "fonts" / "ttf"
|
| | | return ImageFont.truetype(str(d / ("DejaVuSans-Bold.ttf" if bold else "DejaVuSans.ttf")), size)
|
| | | except Exception:
|
| | | return ImageFont.truetype("arialbd.ttf" if bold else "arial.ttf", size)
|
| | |
|
| | |
|
| | | def walk(n, seed):
|
| | | r = random.Random(seed)
|
| | | v, out = 0.0, []
|
| | | for _ in range(n):
|
| | | v += r.gauss(0.0, 1.0) + 0.05
|
| | | out.append(v)
|
| | | lo, hi = min(out), max(out)
|
| | | return [(x - lo) / (hi - lo) for x in out]
|
| | |
|
| | |
|
| | | def background(size, seed=7):
|
| | | """Faint copies of one series: grey = original, amber = shifted and rescaled copies."""
|
| | | S = size * 2 # supersample
|
| | | img = Image.new("RGBA", (S, S), BG + (255,))
|
| | | base = walk(260, seed)
|
| | | n = len(base)
|
| | | layers = [(GREY, 0.0, 1.00, 0.18, 0.18), (ACCENT, 0.06, 0.80, 0.50, 0.26), (ACCENT, 0.13, 1.25, 0.28, 0.20),
|
| | | (ACCENT, 0.20, 0.55, 0.78, 0.17), (GREY, 0.27, 0.95, 0.66, 0.12), (ACCENT, 0.34, 1.10, 0.90, 0.12)]
|
| | | for col, shift, scale, ymid, alpha in layers:
|
| | | lay = Image.new("RGBA", (S, S), (0, 0, 0, 0))
|
| | | d = ImageDraw.Draw(lay)
|
| | | pts = []
|
| | | for i, v in enumerate(base):
|
| | | x = (i / (n - 1) + shift) * S * 1.1 - S * 0.05
|
| | | y = (ymid - (v - 0.5) * 0.5 * scale) * S
|
| | | pts.append((x, y))
|
| | | d.line(pts, fill=col + (int(255 * alpha * 1.6),), width=max(2, S // 230), joint="curve")
|
| | | img = Image.alpha_composite(img, lay)
|
| | | return img.resize((size, size), Image.LANCZOS)
|
| | |
|
| | |
|
| | | def centered(d, text, f, y, fill, W):
|
| | | w = d.textlength(text, font=f)
|
| | | d.text(((W - w) / 2, y), text, font=f, fill=fill)
|
| | |
|
| | |
|
| | | def hero(size=1024):
|
| | | im = background(size).convert("RGB")
|
| | | d = ImageDraw.Draw(im)
|
| | | centered(d, "Tick Forge", font(int(size * 0.115)), size * 0.415, (255, 255, 255), size)
|
| | | centered(d, "custom symbols for the Strategy Tester", font(int(size * 0.036), bold=False), size * 0.565, ACCENT, size)
|
| | | return im
|
| | |
|
| | |
|
2026-11-04 16:28:42 +01:00 | | | def banner(w=1200, h=628):
|
| | | """Landscape card (article / social preview): the square background stretched wide, wordmark + tagline."""
|
| | | big = background(w, seed=7)
|
| | | top = (w - h) // 2
|
| | | im = big.crop((0, top, w, top + h)).convert("RGB")
|
| | | d = ImageDraw.Draw(im)
|
| | | centered(d, "Tick Forge", font(int(h * 0.20)), h * 0.30, (255, 255, 255), w)
|
| | | centered(d, "custom symbols for the Strategy Tester", font(int(h * 0.062), bold=False), h * 0.58, ACCENT, w)
|
| | | return im
|
| | |
|
| | |
|
2026-11-04 16:02:05 +01:00 | | | def logo(size):
|
| | | im = background(size, seed=7).convert("RGB")
|
| | | d = ImageDraw.Draw(im)
|
| | | f = font(int(size * 0.27))
|
| | | centered(d, "Tick", f, size * 0.20, (255, 255, 255), size)
|
| | | centered(d, "Forge", f, size * 0.50, ACCENT, size)
|
| | | return im
|
| | |
|
| | |
|
| | | if __name__ == "__main__":
|
| | | hero().save(OUT / "hero_1024.png")
|
2026-11-04 16:28:42 +01:00 | | | banner().save(OUT / "banner_1200x628.png")
|
2026-11-04 16:02:05 +01:00 | | | big = logo(800)
|
| | | for s in (200, 140, 60):
|
| | | big.resize((s, s), Image.LANCZOS).save(OUT / ("logo_%d.png" % s))
|
| | | print("wrote", OUT)
|