use crate::utils::string_to_color; use std::f32::consts::PI; use iced::Vector; use iced::mouse; use iced::widget::canvas; use iced::widget::canvas::Stroke; use iced::widget::canvas::Style; use iced::{Color, Rectangle, Renderer, Theme}; use kira::sound::static_sound::StaticSoundData; use serde::{Deserialize, Serialize}; pub trait RotationExt { fn rotate(&mut self, teta: f32) -> Self; } impl RotationExt for Vector { fn rotate(&mut self, teta: f32) -> Vector { Vector::new( self.x * teta.cos() - self.y * teta.sin(), self.x * teta.sin() + self.y * teta.cos(), ) } } #[derive(Serialize, Deserialize)] pub struct PolygonFrame { pub teta: f32, pub polygons: Vec, } impl PolygonFrame { pub fn all_sound_to_play_btw(&self, before: f32, after: f32) -> Vec<&StaticSoundData> { let mut all_sound: Vec<&StaticSoundData> = vec![]; for poly in &self.polygons { all_sound.extend(poly.sound_to_play_btw(before, after)); } all_sound } pub fn update(&mut self) { for poly in &mut self.polygons { poly.update(); } } pub fn default() -> PolygonFrame { PolygonFrame { teta: 0.0, polygons: vec![], } } } impl canvas::Program for PolygonFrame { // No internal state type State = (); fn draw( &self, _state: &(), renderer: &Renderer, _theme: &Theme, bounds: Rectangle, _cursor: mouse::Cursor, ) -> Vec { let mut frame = canvas::Frame::new(renderer, bounds.size()); let radius = frame.size().width.min(frame.size().height) / 2.0 - 32.0; let mut vec = Vector::new(0.0, -radius); let c = frame.center(); // Draw Circle let circle = canvas::Path::circle(frame.center(), radius); frame.stroke( &circle, Stroke { width: 6.0, style: Style::Solid(Color::from_rgba(0.0, 0.0, 0.0, 1.0)), ..Stroke::default() }, ); // Draw all polygons by there points for poly in &self.polygons { let l = poly.points_teta.len(); let mut line: canvas::Path; let mut t1: f32; let mut t2: f32; for i in 0..l { t1 = poly.points_teta[i] + poly.global_teta; t2 = poly.points_teta[(i + 1) % l] + poly.global_teta; line = canvas::Path::line(c + vec.rotate(t1), c + vec.rotate(t2)); frame.stroke( &line, Stroke { width: 4.0, style: Style::Solid(poly.color.clone()), ..Stroke::default() }, ); } } // Draw the red dot on the current position let dot = canvas::Path::circle(frame.center() + vec.rotate(self.teta), 5.0); frame.fill(&dot, Color::from_rgb(1.0, 0.0, 0.0)); // Draw the frame frame.stroke_rectangle( iced::Point { x: 0.0, y: 0.0 }, frame.size(), Stroke { width: 16.0, ..Stroke::default() }, ); // Then, we produce the geometry vec![frame.into_geometry()] } } #[derive(Debug, Serialize, Deserialize)] pub struct Polygon { pub global_teta: f32, pub points_teta: Vec, #[serde(skip, default = "dummy_sound")] pub sound: StaticSoundData, pub sound_name: String, pub name: String, #[serde(skip)] pub color: Color, pub color_name: String, } #[warn(dead_code)] impl Polygon { pub fn update(&mut self) { let path = format!("./assets/{0}", &self.sound_name); self.sound = StaticSoundData::from_file(&path).expect("fail to load the sound"); self.color = string_to_color(&self.color_name); } pub fn sound_to_play_btw(&self, before: f32, after: f32) -> Vec<&StaticSoundData> { let mut sound_to_play: Vec<&StaticSoundData> = vec![]; if after < before { sound_to_play = self.sound_to_play_btw(before, 2.0 * PI); sound_to_play.extend(self.sound_to_play_btw(0.0, after)); return sound_to_play; } let mut p_g; for p in self.points_teta.clone() { p_g = (p + self.global_teta) % (2.0 * PI); if before <= p_g && p_g <= after { sound_to_play.push(&self.sound); } } sound_to_play } pub fn n_gon(teta: f32, n_side: u8, sound: StaticSoundData) -> Self { let mut v: Vec = Vec::with_capacity(n_side as usize); for i in 0..n_side { v.push((i as f32 * 2.0 * PI) / n_side as f32); } Polygon { global_teta: teta, points_teta: v, sound: sound, sound_name: "tick.ogg".to_string(), color_name: "Black".to_string(), name: "".to_string(), color: Color::BLACK, } } pub fn segment(teta: f32, sound: StaticSoundData) -> Self { Polygon::n_gon(teta, 2, sound) } pub fn triangle(teta: f32, sound: StaticSoundData) -> Self { Polygon::n_gon(teta, 3, sound) } pub fn square(teta: f32, sound: StaticSoundData) -> Self { Polygon::n_gon(teta, 4, sound) } pub fn nr_6_in_30(teta: f32, sound: StaticSoundData) -> Self { Polygon { sound: sound, name: "".to_string(), color: Color::BLACK, global_teta: teta, sound_name: "tick.ogg".to_string(), color_name: "Black".to_string(), points_teta: vec![ 2.0 * 5.0 * PI / 30.0, 2.0 * 6.0 * PI / 30.0, 2.0 * 12.0 * PI / 30.0, 2.0 * 18.0 * PI / 30.0, 2.0 * 24.0 * PI / 30.0, 2.0 * 25.0 * PI / 30.0, ], } } pub fn nr_7_in_30(teta: f32, sound: StaticSoundData) -> Self { Polygon { sound: sound, name: "".to_string(), color: Color::BLACK, sound_name: "tick.ogg".to_string(), color_name: "Black".to_string(), global_teta: teta, points_teta: vec![ 0.0, 2.0 * 6.0 * PI / 30.0, 2.0 * 7.0 * PI / 30.0, 2.0 * 13.0 * PI / 30.0, 2.0 * 17.0 * PI / 30.0, 2.0 * 23.0 * PI / 30.0, 2.0 * 24.0 * PI / 30.0, ], } } pub fn nr_8_in_30(teta: f32, sound: StaticSoundData) -> Self { Polygon { sound: sound, name: "".to_string(), color: Color::BLACK, sound_name: "tick.ogg".to_string(), color_name: "Black".to_string(), global_teta: teta, points_teta: vec![ 2.0 * PI / 30.0, 2.0 * 5.0 * PI / 30.0, 2.0 * 11.0 * PI / 30.0, 2.0 * 12.0 * PI / 30.0, 2.0 * 18.0 * PI / 30.0, 2.0 * 19.0 * PI / 30.0, 2.0 * 25.0 * PI / 30.0, 2.0 * 29.0 * PI / 30.0, ], } } pub fn nr_9_in_30(teta: f32, sound: StaticSoundData) -> Self { Polygon { sound: sound, name: "".to_string(), color: Color::BLACK, sound_name: "tick.ogg".to_string(), color_name: "Black".to_string(), global_teta: teta, points_teta: vec![ 0.0, 2.0 * PI / 30.0, 2.0 * 7.0 * PI / 30.0, 2.0 * 11.0 * PI / 30.0, 2.0 * 13.0 * PI / 30.0, 2.0 * 17.0 * PI / 30.0, 2.0 * 19.0 * PI / 30.0, 2.0 * 23.0 * PI / 30.0, 2.0 * 29.0 * PI / 30.0, ], } } pub fn nr_8_in_42(teta: f32, sound: StaticSoundData) -> Self { Polygon { sound: sound, name: "".to_string(), color: Color::BLACK, sound_name: "tick.ogg".to_string(), color_name: "Black".to_string(), global_teta: teta, points_teta: vec![ 2.0 * 3.0 * PI / 42.0, 2.0 * 9.0 * PI / 42.0, 2.0 * 14.0 * PI / 42.0, 2.0 * 15.0 * PI / 42.0, 2.0 * 27.0 * PI / 42.0, 2.0 * 28.0 * PI / 42.0, 2.0 * 33.0 * PI / 42.0, 2.0 * 39.0 * PI / 42.0, ], } } pub fn nr_9_in_42(teta: f32, sound: StaticSoundData) -> Self { Polygon { sound: sound, name: "".to_string(), color: Color::BLACK, sound_name: "tick.ogg".to_string(), color_name: "Black".to_string(), global_teta: teta, points_teta: vec![ 0.0, 2.0 * 6.0 * PI / 42.0, 2.0 * 11.0 * PI / 42.0, 2.0 * 12.0 * PI / 42.0, 2.0 * 17.0 * PI / 42.0, 2.0 * 25.0 * PI / 42.0, 2.0 * 30.0 * PI / 42.0, 2.0 * 31.0 * PI / 42.0, 2.0 * 36.0 * PI / 42.0, ], } } pub fn nr_10a_in_42(teta: f32, sound: StaticSoundData) -> Self { Polygon { sound: sound, name: "".to_string(), color: Color::BLACK, sound_name: "tick.ogg".to_string(), color_name: "Black".to_string(), global_teta: teta, points_teta: vec![ 2.0 * 6.0 * PI / 42.0, 2.0 * 7.0 * PI / 42.0, 2.0 * 11.0 * PI / 42.0, 2.0 * 12.0 * PI / 42.0, 2.0 * 17.0 * PI / 42.0, 2.0 * 25.0 * PI / 42.0, 2.0 * 30.0 * PI / 42.0, 2.0 * 31.0 * PI / 42.0, 2.0 * 35.0 * PI / 42.0, 2.0 * 36.0 * PI / 42.0, ], } } pub fn nr_10b_in_42(teta: f32, sound: StaticSoundData) -> Self { Polygon { sound: sound, name: "".to_string(), color: Color::BLACK, sound_name: "tick.ogg".to_string(), color_name: "Black".to_string(), global_teta: teta, points_teta: vec![ 0.0, 2.0 * 1.0 * PI / 42.0, 2.0 * 5.0 * PI / 42.0, 2.0 * 13.0 * PI / 42.0, 2.0 * 18.0 * PI / 42.0, 2.0 * 19.0 * PI / 42.0, 2.0 * 24.0 * PI / 42.0, 2.0 * 29.0 * PI / 42.0, 2.0 * 30.0 * PI / 42.0, 2.0 * 41.0 * PI / 42.0, ], } } } fn dummy_sound() -> StaticSoundData { StaticSoundData::from_file("assets/tick.ogg").expect("Fail to load audio") }