Drawbot: Let's hack something cute (2025)

https://news.ycombinator.com/rss Hits: 3
Summary

def write_svg(strokes, out: Path, flip_y=True, stroke_w=2): bb = bbox(strokes) if not bb: return False xmin, ymin, xmax, ymax = bb w, h = xmax - xmin + 1, ymax - ymin + 1 with out.open("w", encoding="utf-8") as f: if flip_y: f.write(f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="{xmin} {ymin} {w} {h}" ' f'width="{w}" height="{h}" stroke="black" fill="none" stroke-width="{stroke_w}">\n') f.write(f' <g transform="translate(0,{ymin + ymax}) scale(1,-1)">\n') for s in strokes: f.write(' <path d="M{},{} {}"/>\n'.format( s[0][0], s[0][1], " ".join(f"L{x},{y}" for x, y in s[1:]))) f.write(' </g>\n</svg>\n') else: f.write(f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="{xmin} {ymin} {w} {h}" ' f'width="{w}" height="{h}" stroke="black" fill="none" stroke-width="{stroke_w}">\n') for s in strokes: f.write(' <path d="M{},{} {}"/>\n'.format( s[0][0], s[0][1], " ".join(f"L{x},{y}" for x, y in s[1:]))) f.write('</svg>\n') return True def main(): ap = argparse.ArgumentParser(description="Extract SVGs from fixed slots") ap.add_argument("bin", type=Path, help="full flash dump (e.g., full.bin)") ap.add_argument("--out", type=Path, default=Path("svgs"), help="output directory") ap.add_argument("--base", type=lambda x:int(x,0), default=0x04, help="first slot start offset (default 0x04)") ap.add_argument("--slot", type=lambda x:int(x,0), default=0xEA60, help="slot size (default 0xEA60)") ap.add_argument("--count", type=int, default=None, help="number of slots (default: autodetect from file size)") ap.add_argument("--min-pts", type=int, default=2, help="min points to export (default 2)") ap.add_argument("--no-flip-y", action="store_true", help="do not flip Y axis") args = ap.parse_args() data = args.bin.read_bytes() size = len(data) if args.count is None: # Best-effort autodetect usable = max(0, size - args.base) args.count = usable // args.slot args.out.mkdir(parents=True, exist_ok=True) exported = 0 for i in range(args.count): off = args.base + i * args.slot if off >= size: break...

First seen: 2026-01-16 22:22

Last seen: 2026-01-17 00:22