Files
Polymusic/src/polygon_draw.rs
2025-08-02 18:10:22 +02:00

374 lines
11 KiB
Rust

use crate::utils::string_to_color;
use crate::color::color_serde;
use std::f32::consts::PI;
use iced::mouse;
use iced::widget::canvas;
use iced::widget::canvas::{Frame, Stroke, Style};
use iced::Size;
use iced::Vector;
use iced::{Color, Point, 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, Clone)]
pub struct PolygonFrame {
pub teta: f32,
pub polygons: Vec<Polygon>,
}
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![],
}
}
pub fn draw_in_frame(&self, frame: &mut Frame, size: Size) {
let radius = size.height / 2.0 - (size.height / 10.);
let mut vec = Vector::new(0.0, -radius);
let c = Point {
x: size.width / 2.,
y: size.height / 2.,
};
// Draw Circle
let circle = canvas::Path::circle(c, radius);
frame.stroke(
&circle,
Stroke {
width: 6.0,
style: Style::Solid(Color::from_rgba8(210, 193, 182, 0.25)),
..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;
let mut color: Color;
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));
color = poly.color.clone();
color.a = 0.25;
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), 10.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 },
size,
Stroke {
width: 8.0,
style: Style::Solid(Color::from_rgb8(207, 74, 28)),
..Stroke::default()
},
);*/
}
}
impl<Message> canvas::Program<Message> for PolygonFrame {
// No internal state
type State = ();
fn draw(
&self,
_state: &(),
renderer: &Renderer,
_theme: &Theme,
bounds: Rectangle,
_cursor: mouse::Cursor,
) -> Vec<canvas::Geometry> {
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();
frame.fill_rectangle(
iced::Point { x: 0., y: 0. },
frame.size(),
Color::from_rgb8(27, 60, 83),
);
// Draw Circle
let circle = canvas::Path::circle(frame.center(), radius);
frame.stroke(
&circle,
Stroke {
width: 6.0,
style: Style::Solid(Color::from_rgb8(210, 193, 182)),
..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), 10.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,
style: Style::Solid(Color::from_rgb8(207, 74, 28)),
..Stroke::default()
},
);
// Then, we produce the geometry
vec![frame.into_geometry()]
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Polygon {
pub global_teta: f32,
pub points_teta: Vec<f32>,
#[serde(skip, default = "dummy_sound")]
pub sound: StaticSoundData,
pub sound_name: String,
pub name: String,
#[serde(with = "color_serde")]
pub color: Color,
#[serde(skip)]
pub show_color_picker: bool,
}
#[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");
}
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 default(sound: StaticSoundData) -> Self {
Polygon {
global_teta: 0.,
points_teta: vec![],
sound: sound,
sound_name: "tick.ogg".to_string(),
name: "".to_string(),
color: Color::BLACK,
show_color_picker: false,
}
}
pub fn n_gon(n_side: u8, sound: StaticSoundData) -> Self {
let mut v: Vec<f32> = 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);
}
let mut p = Polygon::default(sound);
p.points_teta = v;
p
}
pub fn segment(sound: StaticSoundData) -> Self {
Polygon::n_gon(2, sound)
}
pub fn triangle(sound: StaticSoundData) -> Self {
Polygon::n_gon(3, sound)
}
pub fn square(sound: StaticSoundData) -> Self {
Polygon::n_gon(4, sound)
}
pub fn nr_6_in_30(sound: StaticSoundData) -> Self {
let mut p = Polygon::default(sound);
p.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,
];
p
}
pub fn nr_7_in_30(sound: StaticSoundData) -> Self {
let mut p = Polygon::default(sound);
p.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,
];
p
}
pub fn nr_8_in_30(sound: StaticSoundData) -> Self {
let mut p = Polygon::default(sound);
p.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,
];
p
}
pub fn nr_9_in_30(sound: StaticSoundData) -> Self {
let mut p = Polygon::default(sound);
p.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,
];
p
}
pub fn nr_8_in_42(sound: StaticSoundData) -> Self {
let mut p = Polygon::default(sound);
p.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,
];
p
}
pub fn nr_9_in_42(sound: StaticSoundData) -> Self {
let mut p = Polygon::default(sound);
p.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,
];
p
}
pub fn nr_10a_in_42(sound: StaticSoundData) -> Self {
let mut p = Polygon::default(sound);
p.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,
];
p
}
pub fn nr_10b_in_42(sound: StaticSoundData) -> Self {
let mut p = Polygon::default(sound);
p.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,
];
p
}
}
fn dummy_sound() -> StaticSoundData {
StaticSoundData::from_file("assets/tick.ogg").expect("Fail to load audio")
}