Skip to content

Instantly share code, notes, and snippets.

@ento
Created March 19, 2025 01:57
Show Gist options
  • Save ento/2e369d418b5dff7b7fdf7bf76260616e to your computer and use it in GitHub Desktop.
Save ento/2e369d418b5dff7b7fdf7bf76260616e to your computer and use it in GitHub Desktop.
"""
Generate a set of emoji images depicting various points on a hill.
Assumes pillow is available
https://pypi.org/project/pillow/
Image size is based on the size recommended by Notion
"""
import math
import sys
from PIL import Image, ImageDraw
def hill_curve_func(width: int, height: int, x: float) -> float:
x_center = width // 2
bottom_margin = width // 6
# Primary Gaussian curve
primary_curve_height = width * 1.2
primary_curve_bell_width = width // 6
# Subtract a smaller Gaussian curve to flatten the primary curve a bit
damper_curve_height = width // 2
damper_curve_bell_width = width // 8
return (
height
- bottom_margin
- (
primary_curve_height * math.e ** (-(((x - x_center) ** 2 / (2 * primary_curve_bell_width**2))))
- damper_curve_height * math.e ** (-(((x - x_center) ** 2 / (2 * damper_curve_bell_width**2))))
)
)
def draw_hill_curve(d: ImageDraw.ImageDraw):
width, height = d.im.size
for x1 in range(1, width):
x0 = x1 - 1
y0 = hill_curve_func(width, height, x0)
y1 = hill_curve_func(width, height, x1)
d.line((x0, y0, x1, y1), fill=0, width=width // 30)
def draw_progress_dot(d: ImageDraw.ImageDraw, progress: float):
"""
:param progress: A float between 0 and 1, inclusive.
"""
width, height = d.im.size
x = width * progress
y = hill_curve_func(width, height, x)
radius = width // 9
d.circle((x, y), radius, fill=(0, 168, 211), outline=None, width=0)
def make_emoji(width: int, height: int, progress: float):
"""
:param progress: A float between 0 and 1, inclusive.
"""
# To draw a smooth hill curve, we draw on a bigger image and scale it down
scale = 8
upscaled_width, upscaled_height = width * scale, height * scale
image = Image.new("RGB", (upscaled_width, upscaled_height), (255, 255, 255))
draw_upscaled = ImageDraw.Draw(image)
draw_hill_curve(draw_upscaled)
# Draw the progress dot
final_image = image.resize((width, height), resample=Image.LANCZOS)
draw_final = ImageDraw.Draw(final_image)
draw_progress_dot(draw_final, progress=progress)
return final_image
def main():
emoji_width = 280
emoji_height = 280
dot_count = 7
for i in range(dot_count):
progress = (1 + i) / (dot_count + 1)
emoji = make_emoji(emoji_width, emoji_height, progress)
with open(f"hill_{i + 1}.png", "wb") as f:
emoji.save(f, "PNG")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment