import itertools from pathlib import Path import logging from perlin import CoordsGenerator import svg import json import numpy as np RGen = np.random.default_rng() heartshape = [ svg.M(1, 3), svg.Arc(2, 2, 0, False, True, x=5, y=3), svg.Arc(2, 2, 0, False, True, x=9, y=3), svg.Q(9, 6, 5, 9), svg.Q(1, 6, 1, 3), svg.Z(), ] heartshape2 = [ svg.M(-4.913, -10.043), svg.Q(-1.968, -10.043, -0.404, -6.332), svg.Q(-0.024, -5.252, -0.024, -4.929), svg.Q(0.054, -4.929, 0.231, -5.874), svg.Q(1.743, -10.043, 4.913, -10.043), svg.Q(7.734, -10.043, 9.384, -7.196), svg.Q(9.894, -6.142, 9.894, -5.062), svg.L(9.894, -4.885), svg.Q(9.894, -1.404, 6.913, 1.715), svg.Q(0.823, 9.153, 0.015, 10.043), svg.L(-0.067, 10.043), svg.L(-8.274, -0.069), svg.Q(-9.894, -2.471, -9.894, -4.929), svg.Q(-9.894, -7.762, -7.207, -9.438), svg.Q(-6.114, -10.043, -4.913, -10.043), svg.Z(), svg.M(-9.203, -5.062), svg.L(-9.203, -4.846), svg.Q(-9.203, -1.892, -6.395, 1.188), svg.L(0.015, 9.015), svg.L(0.054, 9.015), svg.L(7.803, -0.419), svg.Q(9.207, -2.592, 9.207, -4.63), svg.L(9.207, -5.144), svg.Q(9.207, -7.464, 6.654, -8.976), svg.Q(5.7, -9.356, 4.913, -9.356), svg.Q(1.35, -9.356, 0.192, -3.322), svg.Q(0.054, -2.808, 0.054, -2.458), svg.L(-0.024, -2.458), svg.Q(-0.024, -3.512, -0.996, -6.332), svg.Q(-2.361, -9.356, -4.913, -9.356), svg.Q(-7.259, -9.356, -8.706, -6.967), svg.Q(-9.203, -6.021, -9.203, -5.062), svg.Z(), ] clubs = [ svg.M(0.005, -2.647), svg.Q(0.765, -2.647, 1.122, -1.841), svg.Q(1.169, -1.657, 1.179, -1.458), svg.Q(1.179, -1.043, 0.753, -0.475), svg.L(0.541, -0.263), svg.L(0.551, -0.263), svg.Q(0.814, -0.362, 1.112, -0.362), svg.L(1.179, -0.362), svg.Q(2.024, -0.362, 2.365, 0.355), svg.Q(2.446, 0.589, 2.446, 0.802), svg.Q(2.446, 1.53, 1.705, 1.899), svg.Q(1.563, 1.941, 1.311, 1.963), svg.Q(0.452, 1.963, 0.094, 0.991), svg.L(0.083, 0.991), svg.L(0.083, 1.022), svg.Q(0.083, 2.24, 0.374, 2.626), svg.L(0.374, 2.647), svg.L(-0.364, 2.647), svg.L(-0.385, 2.626), svg.Q(-0.083, 2.172, -0.083, 0.991), svg.L(-0.083, 0.979), svg.L(-0.095, 0.979), svg.Q(-0.449, 1.963, -1.319, 1.963), svg.Q(-1.997, 1.963, -2.358, 1.25), svg.Q(-2.446, 1.008, -2.446, 0.802), svg.Q(-2.446, -0.024, -1.574, -0.32), svg.Q(-1.339, -0.362, -1.159, -0.362), svg.L(-1.127, -0.362), svg.Q(-0.821, -0.362, -0.544, -0.263), svg.L(-0.534, -0.263), svg.L(-0.534, -0.273), svg.Q(-0.833, -0.525, -0.935, -0.769), svg.Q(-1.148, -1.16, -1.148, -1.447), svg.L(-1.148, -1.497), svg.Q(-1.148, -2.21, -0.385, -2.579), svg.Q(-0.133, -2.647, 0.005, -2.647), svg.Z(), ] spades = [] diamond = [ svg.M(0.01, -7.2), svg.L(4.86, -0.9), svg.Q(5.53, -0.08, 5.53, 0.05), svg.L(0.01, 7.2), svg.Q(-0.11, 7.12, -5.53, 0.05), svg.Q(-5.53, -0.08, -4.46, -1.42), svg.L(0.01, -7.2), svg.Z(), ] "♠" "♣" "♥" def openmoji(marker: str, x, y, s, c): if len(marker) != 1: return file = hex(ord(marker))[2:] file = Path(f"openmoji/{file}.svg") if file.is_file(): return svg.Text(text=marker, x=x, y=y, class_=["c"+c[1:], hex(int(s*100))[2:]]) # return svg.Image(href=str(file), transform=[svg.Translate(x=x, y=y), svg.Scale(s)]) def markertoSVG(marker, x, y, c, s): if marker == ".": return svg.Circle(cx=x, cy=y, fill=c, r=np.sqrt(s)) elif marker == "o": return svg.Circle( cx=x, cy=y, stroke=c, fill="none", stroke_width=np.sqrt(s / 4), r=np.sqrt(s), ) elif marker == "+": shift = np.sqrt(s) return svg.Path( stroke=c, stroke_width=np.sqrt(s) / 4, stroke_linecap="round", d=[ svg.M(x - shift, y), # horizontal line svg.L(x + shift, y), svg.M(x, y - shift), # vertical line svg.L(x, y + shift), ], ) elif marker == "t": shift = np.sqrt(s) return svg.Polygon( stroke=c, stroke_width=np.sqrt(s) / 4, stroke_linejoin="round", points=[ x + shift * np.sqrt(3) / 2, y - shift / 2, x - shift * np.sqrt(3) / 2, y - shift / 2, x, y + shift, ], transform=[svg.Rotate(RGen.random() * 360, x, y)], ) elif marker == "Y": shift = np.sqrt(s) return svg.Path( stroke=c, stroke_width=np.sqrt(s) / 4, stroke_linecap="round", d=[ svg.M(x + shift * np.sqrt(3) / 2, y - shift / 2), # / line svg.L(x, y), svg.M(x - shift * np.sqrt(3) / 2, y - shift / 2), # / line svg.L(x, y), svg.M(x, y + shift), # / line svg.L(x, y), ], transform=[svg.Rotate(RGen.random() * 360, x, y)], ) elif marker == "P": shift = np.sqrt(s) return svg.Path( stroke=c, stroke_width=np.sqrt(s) * 2 / 3, d=[ svg.M(x - shift, y), # horizontal line svg.L(x + shift, y), svg.M(x, y - shift), # vertical line svg.L(x, y + shift), ], ) elif marker == "-": shift = np.sqrt(s) return svg.Path( stroke=c, stroke_width=np.sqrt(s) / 4, d=[ svg.M(x - shift, y), # horizontal line svg.L(x + shift, y), ], ) elif marker == "|": shift = np.sqrt(s) return svg.Path( stroke=c, stroke_width=np.sqrt(s) / 4, d=[ svg.M(x, y - shift), # vertical line svg.L(x, y + shift), ], ) elif marker == "s": shift = np.sqrt(s) * 2 return svg.Rect(x=x, y=y, width=shift, height=shift, fill=c) elif marker == "S": s = np.sqrt(s) * 2 return svg.Rect(x=x, y=y, width=s, height=s, fill=c, rx=s / 3, ry=s / 3) elif marker == "♦": s = np.sqrt(s) / 8 return svg.Path( d=diamond, fill=c, transform=[svg.Translate(x=x, y=y), svg.Scale(s)], ) elif marker == "♠": return openmoji(marker, x, y, s, c) elif marker == "♣": s = np.sqrt(s) / 2 return svg.Path( d=clubs, fill=c, transform=[svg.Translate(x=x, y=y), svg.Scale(s)], ) elif marker == "♦": s = np.sqrt(s) / 8 return svg.Path( d=diamond, fill=c, transform=[svg.Translate(x=x, y=y), svg.Scale(s)], ) elif marker == "♡": s = np.sqrt(s) / 4 return svg.Path( d=heartshape2, fill="none", stroke=c, stroke_width=s / 4, transform=[svg.Translate(x=x, y=y), svg.Scale(s / 2)], ) elif marker == "♥": s = np.sqrt(s) / 4 return svg.Path( d=heartshape, fill=c, transform=[svg.Translate(x=x - 5, y=y - 9), svg.Scale(s)], ) elif marker == "e": s = np.sqrt(s * 2) return svg.Ellipse( cx=x, cy=y, fill=c, rx=s, ry=s / 2, transform=[svg.Rotate(RGen.random() * 360, x, y)], ) elif marker == "x": shift = np.sqrt(s) scale = np.sqrt(2) / 2 return svg.Path( stroke=c, stroke_width=np.sqrt(s) / 4, stroke_linecap="round", d=[ svg.M(x - shift * scale, y - shift * scale), # / line svg.L(x + shift * scale, y + shift * scale), svg.M(x - shift * scale, y + shift * scale), svg.L(x + shift * scale, y - shift * scale), # / line ], ) elif marker == "*": shift = np.sqrt(s) return [ svg.Path( stroke=c, stroke_width=np.sqrt(s) / 4, stroke_linecap="round", transform=[svg.Rotate(angle, x, y)], d=[ svg.M(x - shift, y), svg.L(x + shift, y), ], ) for angle in (0, 60, -60) ] def make_wallpaper( speckle_colours: str, density: float = 0.12, size: float = 3, fileformat: str = "svg", orientation: str = "landscape", filename: str = "", markers: str = ".", perlin: bool = True, ): speckle_colours = speckle_colours.split(",") background = speckle_colours.pop(0) if orientation == "portrait": x, y = (1080, 1920) elif orientation == "landscape": x, y = (1920, 1080) elif "x" in orientation: resolution = orientation.split("x") if len(resolution) != 2: logging.critical("input resolution has more or less than 2 dimensions") return x, y = resolution if all([x.isdigit(), y.isdigit()]): x, y = int(x), int(y) else: return else: x, y = (1920, 1080) speckles_per_colour = int(x / 128 * y / 128 * density / len(markers)) if perlin: gen = CoordsGenerator(x, y) elements = [svg.Rect(width=x, height=y, color=background)] style = svg.Style( text="\n".join( [f".c{colour[1:]} {{ fill: {colour}; }}" for colour in speckle_colours] ) ) elements.append(style) style = svg.Style( text="\n".join( [ f".{hex(int(size*100))[2:]} {{ font-size: {int(np.sqrt(size)*16)}px; }}" for size in np.logspace(0, size, 10, base=np.exp(2)) ] ) ) elements.append(style) for colour, marker, size in itertools.product( speckle_colours, markers, np.logspace(0, size, 10, base=np.exp(2)), ): if perlin: xs, ys = gen.pick(speckles_per_colour) else: xs, ys = zip( RGen.random(speckles_per_colour) * x, RGen.random(speckles_per_colour) * y, ) elements.extend( [markertoSVG(marker, x, y, colour, size) for x, y in zip(xs, ys)] ) content = svg.SVG(width=x, height=y, elements=elements) with open(filename, "wt") as f: f.write(content.as_str()) if __name__ == "__main__": palettes = [ {"colours": ["#DE4A21", "#6F04F2", "#490B1C", "#4a9c9e"], "votes": 1000000}, {"colours": ["#eee4ab", "#e5cb9f", "#99c4c8", "#68a7ad"], "votes": 100000}, {"colours": ["#e4d192", "#cba0ae", "#af7ab3", "#80558c"], "votes": 100000}, {"colours": ["#eeeeee", "#e1d4bb", "#cbb279", "#537188"], "votes": 100000}, {"colours": ["#c0dbea", "#ba90c6", "#e8a0bf", "#fdf4f5"], "votes": 100000}, {"colours": ["#f5ffc9", "#b3e5be", "#a86464", "#804674"], "votes": 100000}, {"colours": ["#ffde7d", "#f6416c", "#f8f3d4", "#00b8a9"], "votes": 100000}, {"colours": ["#53354a", "#903749", "#e84545", "#2b2e4a"], "votes": 100000}, {"colours": ["#967e76", "#d7c0ae", "#eee3cb", "#b7c4cf"], "votes": 100000}, {"colours": ["#fc5185", "#f5f5f5", "#3fc1c9", "#364f6b"], "votes": 100000}, {"colours": ["#eaeaea", "#ff2e63", "#252a34", "#08d9d6"], "votes": 100000}, {"colours": ["#eeeeee", "#00adb5", "#393e46", "#222831"], "votes": 100000}, {"colours": ["#2cd3e1", "#a459d1", "#f266ab", "#ffb84c"], "votes": 100000}, {"colours": ["#ffe194", "#e8f6ef", "#1b9c85", "#4c4c6d"], "votes": 100000}, {"colours": ["#146c94", "#19a7ce", "#b0daff", "#feff86"], "votes": 100000}, {"colours": ["#4c3d3d", "#c07f00", "#ffd95a", "#fff7d4"], "votes": 100000}, {"colours": ["#8bacaa", "#b04759", "#e76161", "#f99b7d"], "votes": 100000}, {"colours": ["#146c94", "#19a7ce", "#afd3e2", "#f6f1f1"], "votes": 100000}, {"colours": ["#9ba4b5", "#212a3e", "#394867", "#f1f6f9"], "votes": 100000}, {"colours": ["#00ffca", "#05bfdb", "#088395", "#0a4d68"], "votes": 100000}, {"colours": ["#7c9070", "#9ca777", "#fee8b0", "#f97b22"], "votes": 100000}, {"colours": ["#002a19", "#000000", "#ffffff", "#e0512f"], "votes": 100000}, ] make_wallpaper( ",".join(["#000000"] + palettes[0]["colours"]), orientation="2880x1920", filename="testing.svg", markers="Yt♠", ) exit() palette_files = ["colorhunt.json"] for palette_file in palette_files: with open(palette_file, "rt") as f: palettes += json.load(f) print(len(palettes)) palettes = [ palette["colours"] for palette in palettes if palette["votes"] > 30000 or len(palette["colours"]) > 5 ] print(len(palettes)) for palette in palettes: night_sky(palette) speckles(palette)