| """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
|
| from pathlib import Path
|
| from PIL import Image, ImageDraw, ImageFont
|
|
|
| OUT = Path(__file__).resolve().parent.parent / "market"
|
| OUT.mkdir(exist_ok=True)
|
| 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
|
|
|
|
|
| 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
|
|
|
|
|
| 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")
|
| banner().save(OUT / "banner_1200x628.png")
|
| big = logo(800)
|
| for s in (200, 140, 60):
|
| big.resize((s, s), Image.LANCZOS).save(OUT / ("logo_%d.png" % s))
|
| print("wrote", OUT)
|