use axum::{extract::Query, response::Html, routing::get, Router}; use cairo::{Context, Format, ImageSurface}; use rand::{rngs::ThreadRng, Rng}; use serde::Deserialize; use std::net::SocketAddr; #[tokio::main] async fn main() { // build our application with a route let app = Router::new().route("/", get(handler)); // run it let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); println!("listening on {}", addr); axum::Server::bind(&addr) .serve(app.into_make_service()) .await .unwrap(); } fn calculate_polygon_corners( center: (f64, f64), num_sides: i32, side_length: f64, rotation_angle_degrees: f64, ) -> Vec<(f64, f64)> { // no idea how this code works, it was written by ChatGPT let mut corners = Vec::new(); let rotation_angle = rotation_angle_degrees.to_radians(); let angle = std::f64::consts::PI / (num_sides as f64); let radius = side_length / (2.0 * angle.cos()); for i in 0..num_sides { let vertex_angle = angle * (2.0 * i as f64 + 1.0 - num_sides as f64) + rotation_angle; let x = center.0 + radius * vertex_angle.sin(); let y = center.1 + radius * vertex_angle.cos(); corners.push((x, y)); } corners } fn set_color_i8((r, g, b): (u8, u8, u8), context: &Context) { context.set_source_rgb(r as f64 / 255.0, g as f64 / 255.0, b as f64 / 255.0); } fn generate_color( rng: &mut ThreadRng, dark_mode: bool, unlike: Option<(u8, u8, u8)>, ) -> (u8, u8, u8) { let criteria = |(r, g, b): (u8, u8, u8)| { let r = r as u16; let g = g as u16; let b = b as u16; if dark_mode && (r + g + b) < 100 { return false; } else if !dark_mode && (r + g + b) > 600 { return false; } if let Some((r1, g1, b1)) = unlike { if (r.abs_diff(r1 as u16) + g.abs_diff(g1 as u16) + b.abs_diff(b1 as u16)) < 200 { return false; } } return true; }; let mut color = rng.gen(); while !criteria(color) { color = rng.gen(); } color } fn draw_segmented_polygon( center: (f64, f64), side_length: f64, context: &Context, dark_mode: bool, ) { let mut rng = rand::thread_rng(); let c1 = generate_color(&mut rng, dark_mode, None); set_color_i8(c1, context); context.new_path(); let corners = calculate_polygon_corners(center, 6, side_length, 30.0); let corner1: u8 = rng.gen_range(0..6); let corner2: u8 = (corner1 + rng.gen_range(2..5)) % 6; let from_corner = std::cmp::min(corner1, corner2); let to_corner = std::cmp::max(corner1, corner2); // draw one side for i in from_corner..(to_corner + 1) { let (x, y) = corners[i as usize]; context.line_to(x, y); } context.close_path(); context.fill().unwrap(); let c2 = generate_color(&mut rng, dark_mode, Some(c1)); set_color_i8(c2, context); context.new_path(); // draw other side for i in to_corner..(from_corner + 1 + 6) { let (x, y) = corners[(i % 6) as usize]; context.line_to(x, y); } context.close_path(); context.fill().unwrap(); } fn default_as_false() -> bool { false } #[derive(Deserialize)] struct ImageProperties { #[serde(default = "default_as_false")] dark_mode: bool, } async fn handler(Query(properties): Query) -> impl axum::response::IntoResponse { let surface = ImageSurface::create(Format::ARgb32, 400, 400).unwrap(); let context = Context::new(&surface).unwrap(); if properties.dark_mode { context.set_source_rgb(0.0, 0.0, 0.0); } else { context.set_source_rgb(1.0, 1.0, 1.0); } context.paint().unwrap(); let corners = calculate_polygon_corners((200.0, 200.0), 6, 200.0, 0.0); for corner in corners { draw_segmented_polygon(corner, 80.0, &context, properties.dark_mode); } let mut data: Vec = Vec::new(); surface.write_to_png(&mut data).unwrap(); ( axum::response::AppendHeaders([(axum::http::header::CONTENT_TYPE, "image/png")]), data, ) }