diff --git a/docs/_static/favicon.svg b/docs/_static/favicon.svg new file mode 100644 index 0000000..06e3966 --- /dev/null +++ b/docs/_static/favicon.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/docs/_static/make_logo.py b/docs/_static/make_logo.py new file mode 100644 index 0000000..c8dacd8 --- /dev/null +++ b/docs/_static/make_logo.py @@ -0,0 +1,80 @@ +# /// script +# requires-python = ">=3.11" +# dependencies = ["resvg-py"] +# /// +"""Draw the diffraxtra logo: diffrax's, plus a plus. + +diffrax's logo is an integral sign cut out of a rounded square. diffraxtra's +cuts a big + out beside it, for the extras. Both marks are vector paths, so the +logo is written as an SVG, sharp at any size; for a bitmap, name a .png and give +its size:: + + uv run docs/_static/make_logo.py # favicon.svg + uv run docs/_static/make_logo.py --size 2048 big.png +""" + +import argparse +from pathlib import Path + +# The integral sign: Material Design Icons' "math-integral", by Pictogrammers, +# under the Apache License 2.0 (https://pictogrammers.com/library/mdi/). The box +# is that set's "math-integral-box", diffrax's logo, as a rounded square. +INTEGRAL = ( + "M11.5 19.1C11.3 20.2 10.9 21 10.2 21.5C9.5 22 8.6 22.1 7.5 21.9C7.1 21.8 " + "6.3 21.7 6 21.5L6.5 20C6.8 20.1 7.4 20.3 7.7 20.3C8.8 20.5 9.4 20 9.6 " + "18.8L12 5.2C12.2 4 12.7 3.2 13.4 2.6C14.1 2.1 15.1 1.9 16.2 2.1C16.6 2.2 " + "17.4 2.3 18 2.6L17.5 4C17.3 3.9 16.6 3.8 16.3 3.7C15 3.5 14.3 4.1 14 " + "5.6L11.5 19.1Z" +) +# In the icons' 24-unit grid: the integral is scaled to fit the box beside the +# plus, which is drawn as two round-capped strokes. +INTEGRAL_AT = "translate(9.2 12) scale(0.78) translate(-12 -12)" +PLUS = (15.6, 12, 2.9, 2.0) # centre x, centre y, arm length, stroke width + +SVG = """\ + + + + + + + + +""" + + +def svg() -> str: + """Return the logo as SVG text.""" + x, y, arm, width = PLUS + plus = f"M{x - arm:g} {y:g}H{x + arm:g}M{x:g} {y - arm:g}V{y + arm:g}" + return SVG.format( + integral=INTEGRAL, integral_at=INTEGRAL_AT, plus=plus, width=width + ) + + +def main() -> None: + """Parse the command line and save the logo.""" + parser = argparse.ArgumentParser(description=__doc__.split("\n")[0]) + parser.add_argument( + "out", + nargs="?", + type=Path, + default=Path(__file__).with_name("favicon.svg"), + help="output file, SVG or PNG by its extension (default: favicon.svg)", + ) + parser.add_argument( + "--size", type=int, default=512, help="pixels per side, for a PNG" + ) + args = parser.parse_args() + + if args.out.suffix == ".svg": + args.out.write_text(svg()) + else: + import resvg_py # noqa: PLC0415 # only a PNG needs a renderer + + png = resvg_py.svg_to_bytes(svg_string=svg(), width=args.size) + args.out.write_bytes(bytes(png)) + + +if __name__ == "__main__": + main()