From 3d32fa6bd8aec4fd01b8f548d89f1f8cb948ed73 Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Sat, 11 Mar 2023 21:44:17 +0100 Subject: [PATCH] Feat: cleanup and favicon --- src/main.rs | 153 ++++-------------------------------------- src/polygon.rs | 115 +++++++++++++++++++++++++++++++ src/routes/favicon.rs | 26 +++++++ src/routes/logo.rs | 42 ++++++++++++ src/routes/mod.rs | 2 + 5 files changed, 197 insertions(+), 141 deletions(-) create mode 100644 src/polygon.rs create mode 100644 src/routes/favicon.rs create mode 100644 src/routes/logo.rs create mode 100644 src/routes/mod.rs diff --git a/src/main.rs b/src/main.rs index cf0626b..092c831 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,19 @@ -use axum::{extract::Query, response::Html, routing::get, Router}; -use cairo::{Context, Format, ImageSurface}; -use rand::{rngs::ThreadRng, Rng}; -use serde::Deserialize; +use axum::Router; use std::net::SocketAddr; +mod polygon; +mod routes; + +#[derive(Clone)] +pub struct SharedState {} + #[tokio::main] async fn main() { - // build our application with a route - let app = Router::new().route("/", get(handler)); + let state = SharedState {}; + let app = Router::new() + .nest("/logo", routes::logo::routes()) + .nest("/favicon.ico", routes::favicon::routes()) + .with_state(state); // run it let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); @@ -17,138 +23,3 @@ async fn main() { .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, - ) -} diff --git a/src/polygon.rs b/src/polygon.rs new file mode 100644 index 0000000..5e690d9 --- /dev/null +++ b/src/polygon.rs @@ -0,0 +1,115 @@ +use cairo::Context; +use rand::{rngs::ThreadRng, Rng}; + +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 +} + +pub fn draw_segmented_polygon( + center: (f64, f64), + side_length: f64, + num_sides: i32, + 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, num_sides, 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(); +} + +pub fn draw_polygon_of_polygons( + center: (f64, f64), + side_length: f64, + num_sides: i32, + context: &Context, + dark_mode: bool, +) { + let corners = calculate_polygon_corners(center, num_sides, side_length, 0.0); + for corner in corners { + draw_segmented_polygon(corner, side_length * 0.4, num_sides, &context, dark_mode); + } +} diff --git a/src/routes/favicon.rs b/src/routes/favicon.rs new file mode 100644 index 0000000..ca255c2 --- /dev/null +++ b/src/routes/favicon.rs @@ -0,0 +1,26 @@ +use axum::{routing::get, Router}; +use cairo::{Context, Format, ImageSurface}; + +use crate::{polygon, SharedState}; + +async fn handler() -> impl axum::response::IntoResponse { + let surface = ImageSurface::create(Format::ARgb32, 100, 100).unwrap(); + let context = Context::new(&surface).unwrap(); + + context.set_source_rgba(0.0, 0.0, 0.0, 0.0); + context.fill().unwrap(); + + polygon::draw_polygon_of_polygons((50.0, 50.0), 65.0, 6, &context, false); + + let mut data: Vec = Vec::new(); + surface.write_to_png(&mut data).unwrap(); + + ( + axum::response::AppendHeaders([(axum::http::header::CONTENT_TYPE, "image/png")]), + data, + ) +} + +pub fn routes() -> Router { + Router::new().route("/", get(handler)) +} diff --git a/src/routes/logo.rs b/src/routes/logo.rs new file mode 100644 index 0000000..ffa38b2 --- /dev/null +++ b/src/routes/logo.rs @@ -0,0 +1,42 @@ +use axum::{extract::Query, routing::get, Router}; +use cairo::{Context, Format, ImageSurface}; +use serde::Deserialize; + +use crate::{polygon, SharedState}; + +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(); + + polygon::draw_polygon_of_polygons((200.0, 200.0), 200.0, 6, &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, + ) +} + +pub fn routes() -> Router { + Router::new().route("/", get(handler)) +} diff --git a/src/routes/mod.rs b/src/routes/mod.rs new file mode 100644 index 0000000..e5ef161 --- /dev/null +++ b/src/routes/mod.rs @@ -0,0 +1,2 @@ +pub mod favicon; +pub mod logo;