13 Commits

Author SHA1 Message Date
9f59e68b2c fix by cargo 2025-07-10 15:09:45 +02:00
607cf17888 add color picker and better buttons 2025-07-10 15:07:52 +02:00
984cec5741 antialiazing 2025-07-10 00:33:29 +02:00
8eb84f6780 del notused font 2025-07-10 00:26:31 +02:00
719f0c1332 fonts and glowup 2025-07-10 00:25:42 +02:00
064190d1a7 add small canvas on time line 2025-07-09 20:50:39 +02:00
a426c7bb98 colorfull 2025-07-09 18:07:53 +02:00
440032ffdb better color 2025-07-09 17:20:00 +02:00
7047679ace add to do list 2025-07-09 15:24:20 +02:00
68f8cff151 padding 2025-07-09 11:21:02 +02:00
e85ed8371d timeline clickable 2025-07-09 11:18:15 +02:00
bf2266955b add remove point 2025-07-09 09:33:26 +02:00
4d31708481 change sounds 2025-07-09 01:35:16 +02:00
59 changed files with 799 additions and 329 deletions

164
Cargo.lock generated
View File

@@ -121,6 +121,12 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04"
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]] [[package]]
name = "android_system_properties" name = "android_system_properties"
version = "0.1.5" version = "0.1.5"
@@ -352,7 +358,7 @@ dependencies = [
"bitflags 2.9.1", "bitflags 2.9.1",
"cexpr", "cexpr",
"clang-sys", "clang-sys",
"itertools", "itertools 0.13.0",
"proc-macro2", "proc-macro2",
"quote", "quote",
"regex", "regex",
@@ -533,6 +539,20 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
name = "chrono"
version = "0.4.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits",
"wasm-bindgen",
"windows-link",
]
[[package]] [[package]]
name = "clang-sys" name = "clang-sys"
version = "1.8.1" version = "1.8.1"
@@ -1548,6 +1568,30 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
[[package]]
name = "iana-time-zone"
version = "0.1.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"log",
"wasm-bindgen",
"windows-core 0.61.2",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
]
[[package]] [[package]]
name = "iced" name = "iced"
version = "0.13.1" version = "0.13.1"
@@ -1562,6 +1606,23 @@ dependencies = [
"thiserror", "thiserror",
] ]
[[package]]
name = "iced_aw"
version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "582c517a94ce3205da98e9c10b26bb71aa36b7d7d084441d826dc912711d1bac"
dependencies = [
"cfg-if",
"chrono",
"getrandom 0.3.3",
"iced",
"iced_fonts 0.1.1",
"itertools 0.14.0",
"num-format",
"num-traits",
"web-time",
]
[[package]] [[package]]
name = "iced_core" name = "iced_core"
version = "0.13.2" version = "0.13.2"
@@ -1582,6 +1643,25 @@ dependencies = [
"web-time", "web-time",
] ]
[[package]]
name = "iced_fonts"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df7deb0800a850ee25c8a42559f72c0f249e577feb3aad37b9b65dc1e517e52a"
dependencies = [
"iced_core",
]
[[package]]
name = "iced_fonts"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7db9d45e87bc3ac22ff82e2d9dea9139f1c055137f1e0f39df3d68110fcee7a9"
dependencies = [
"iced_core",
"iced_widget",
]
[[package]] [[package]]
name = "iced_futures" name = "iced_futures"
version = "0.13.2" version = "0.13.2"
@@ -1757,6 +1837,15 @@ dependencies = [
"either", "either",
] ]
[[package]]
name = "itertools"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
dependencies = [
"either",
]
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "1.0.15" version = "1.0.15"
@@ -2176,6 +2265,16 @@ dependencies = [
"syn 2.0.101", "syn 2.0.101",
] ]
[[package]]
name = "num-format"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3"
dependencies = [
"arrayvec",
"itoa",
]
[[package]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.2.19" version = "0.2.19"
@@ -2729,6 +2828,8 @@ name = "polygomusic"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"iced", "iced",
"iced_aw",
"iced_fonts 0.2.1",
"kira", "kira",
"regex", "regex",
"serde", "serde",
@@ -4170,10 +4271,51 @@ version = "0.54.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65" checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65"
dependencies = [ dependencies = [
"windows-result", "windows-result 0.1.2",
"windows-targets 0.52.6", "windows-targets 0.52.6",
] ]
[[package]]
name = "windows-core"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
dependencies = [
"windows-implement",
"windows-interface",
"windows-link",
"windows-result 0.3.4",
"windows-strings",
]
[[package]]
name = "windows-implement"
version = "0.60.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
]
[[package]]
name = "windows-interface"
version = "0.59.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
]
[[package]]
name = "windows-link"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
[[package]] [[package]]
name = "windows-result" name = "windows-result"
version = "0.1.2" version = "0.1.2"
@@ -4183,6 +4325,24 @@ dependencies = [
"windows-targets 0.52.6", "windows-targets 0.52.6",
] ]
[[package]]
name = "windows-result"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-strings"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
dependencies = [
"windows-link",
]
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.45.0" version = "0.45.0"

View File

@@ -2,6 +2,7 @@
name = "polygomusic" name = "polygomusic"
version = "0.1.0" version = "0.1.0"
edition = "2024" edition = "2024"
build = "build.rs"
[dependencies] [dependencies]
iced = { version = "0.13.1", features = ["canvas", "tokio"] } iced = { version = "0.13.1", features = ["canvas", "tokio"] }
@@ -10,3 +11,5 @@ tokio = {version = "1.45.1", features = ["time"]}
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.140" serde_json = "1.0.140"
regex = "1.11.1" regex = "1.11.1"
iced_aw = {version = "0.12.2", default-features = true}
iced_fonts = "0.2.1"

View File

@@ -24,3 +24,10 @@ The `assets` folder contains all the sounds you can play. You can add more.
- FLAC - FLAC
- WAV - WAV
## To Do List
- [ ] Custom Theme
- [ ] RGB
- [ ] Edit angle by degree
- [ ] Sound on folder
- [ ] Global sound

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
assets/a-3.mp3 Normal file

Binary file not shown.

BIN
assets/a-4.mp3 Normal file

Binary file not shown.

BIN
assets/a-5.mp3 Normal file

Binary file not shown.

BIN
assets/a3.mp3 Normal file

Binary file not shown.

BIN
assets/a4.mp3 Normal file

Binary file not shown.

BIN
assets/a5.mp3 Normal file

Binary file not shown.

BIN
assets/b3.mp3 Normal file

Binary file not shown.

BIN
assets/b4.mp3 Normal file

Binary file not shown.

BIN
assets/b5.mp3 Normal file

Binary file not shown.

BIN
assets/c-3.mp3 Normal file

Binary file not shown.

BIN
assets/c-4.mp3 Normal file

Binary file not shown.

BIN
assets/c-5.mp3 Normal file

Binary file not shown.

BIN
assets/c3.mp3 Normal file

Binary file not shown.

BIN
assets/c4.mp3 Normal file

Binary file not shown.

BIN
assets/c5.mp3 Normal file

Binary file not shown.

BIN
assets/c6.mp3 Normal file

Binary file not shown.

BIN
assets/d-3.mp3 Normal file

Binary file not shown.

BIN
assets/d-4.mp3 Normal file

Binary file not shown.

BIN
assets/d-5.mp3 Normal file

Binary file not shown.

BIN
assets/d3.mp3 Normal file

Binary file not shown.

BIN
assets/d4.mp3 Normal file

Binary file not shown.

BIN
assets/d5.mp3 Normal file

Binary file not shown.

BIN
assets/e3.mp3 Normal file

Binary file not shown.

BIN
assets/e4.mp3 Normal file

Binary file not shown.

BIN
assets/e5.mp3 Normal file

Binary file not shown.

BIN
assets/f-3.mp3 Normal file

Binary file not shown.

BIN
assets/f-4.mp3 Normal file

Binary file not shown.

BIN
assets/f-5.mp3 Normal file

Binary file not shown.

BIN
assets/f3.mp3 Normal file

Binary file not shown.

BIN
assets/f4.mp3 Normal file

Binary file not shown.

BIN
assets/f5.mp3 Normal file

Binary file not shown.

BIN
assets/g-3.mp3 Normal file

Binary file not shown.

BIN
assets/g-4.mp3 Normal file

Binary file not shown.

BIN
assets/g-5.mp3 Normal file

Binary file not shown.

BIN
assets/g3.mp3 Normal file

Binary file not shown.

BIN
assets/g4.mp3 Normal file

Binary file not shown.

BIN
assets/g5.mp3 Normal file

Binary file not shown.

39
build.rs Normal file
View File

@@ -0,0 +1,39 @@
use std::env;
use std::fs;
use std::path::Path;
fn main() {
println!("cargo:rerun-if-changed=assets");
println!("cargo:rerun-if-changed=fonts");
// Chemin vers target/debug ou target/release
let out_dir = env::var("OUT_DIR").unwrap();
let target_dir = Path::new(&out_dir)
.ancestors()
.nth(3) // On remonte pour arriver dans target/debug ou target/release
.unwrap();
// Copie assets/
let _ = fs::remove_dir_all(target_dir.join("assets"));
fs::create_dir_all(target_dir.join("assets")).unwrap();
copy_dir("assets", target_dir.join("assets"));
// Copie fonts/
let _ = fs::remove_dir_all(target_dir.join("fonts"));
fs::create_dir_all(target_dir.join("fonts")).unwrap();
copy_dir("fonts", target_dir.join("fonts"));
}
fn copy_dir(src: impl AsRef<Path>, dst: impl AsRef<Path>) {
for entry in fs::read_dir(src).unwrap() {
let entry = entry.unwrap();
let ty = entry.file_type().unwrap();
let dst_path = dst.as_ref().join(entry.file_name());
if ty.is_dir() {
fs::create_dir_all(&dst_path).unwrap();
copy_dir(entry.path(), &dst_path);
} else {
fs::copy(entry.path(), &dst_path).unwrap();
}
}
}

Binary file not shown.

View File

@@ -4,61 +4,61 @@ use polygon_draw::Polygon;
mod music; mod music;
use music::Music; use music::Music;
mod message;
use message::Message;
mod utils; mod utils;
use utils::str_to_sec; use utils::{is_delta_format_valid, str_to_sec};
use std::fs; use std::fs;
use iced::Element; use iced::widget::{TextInput, column, text};
use iced::{Element, Font, Subscription};
use iced::{ use iced::{
Font, Color, Length, Task, Theme,
widget::{
TextInput, column, text,
text_input::{Icon, Side},
},
};
use iced::{
Length, Task, Theme,
widget::{Column, button, canvas, container, pick_list, row, scrollable, slider}, widget::{Column, button, canvas, container, pick_list, row, scrollable, slider},
}; };
use iced_aw::widget::color_picker;
use regex::Regex;
use std::f32::consts::PI; use std::f32::consts::PI;
use std::time::Instant; use std::time::Instant;
use kira::{ use kira::{
AudioManager, AudioManagerSettings, DefaultBackend, sound::static_sound::StaticSoundData, AudioManager, AudioManagerSettings, DefaultBackend, sound::static_sound::StaticSoundData,
}; };
const FONT_BYTES: &[u8] = include_bytes!("../fonts/EnvyCodeRNerdFontMono-Regular.ttf");
const FONT: Font = Font::with_name("EnvyCodeR Nerd Font Mono");
use crate::utils::delta_to_string; use crate::utils::delta_to_string;
fn main() -> iced::Result { fn main() -> iced::Result {
let polytheme: Theme = {
let back = Color::from_rgb8(39, 63, 79);
let text = Color::from_rgb8(0xD3, 0xD4, 0xD9);
let prim = Color::from_rgb8(230, 82, 31);
let succ = Color::from_rgb8(0xFF, 0xF9, 0xFB);
let dan = Color::from_rgb8(0xBB, 0x0A, 0x21);
Theme::custom(
String::from("PolyTheme"),
iced::theme::Palette {
background: back,
text: text,
primary: prim,
success: succ,
danger: dan,
},
)
};
iced::application("My App", MyApp::update, MyApp::view) iced::application("My App", MyApp::update, MyApp::view)
.theme(|_| Theme::Dark) .theme(move |_| polytheme.clone())
.font(iced_fonts::REQUIRED_FONT_BYTES)
.font(FONT_BYTES)
.default_font(FONT)
.antialiasing(true)
.subscription(MyApp::subscription) .subscription(MyApp::subscription)
.run_with(MyApp::new) .run_with(MyApp::new)
} }
#[derive(Debug, Clone)]
enum Message {
ButtonPressedIncrement,
ButtonPressedDecrement,
Tick,
AddPolygon(String),
ChangeTeta(usize, f32),
Remove(usize),
ChangeColor(usize, String),
ChangeSound(usize, String),
ToggleSavePanel,
Save,
Load,
FileNameChanged(String),
TogglePaused,
SetMusicLength,
LengthChange(String),
ChangeDelta(f32),
AddPoint,
}
struct MyApp { struct MyApp {
music: Music, music: Music,
time_last_frame: Instant, time_last_frame: Instant,
@@ -69,12 +69,16 @@ struct MyApp {
all_saves: Vec<String>, all_saves: Vec<String>,
current_delta: f32, current_delta: f32,
str_music_length: String, str_music_length: String,
str_time: String,
} }
impl MyApp { impl MyApp {
fn new() -> (Self, Task<Message>) { fn new() -> (Self, Task<Message>) {
let manager = AudioManager::<DefaultBackend>::new(AudioManagerSettings::default()) let manager = AudioManager::<DefaultBackend>::new(AudioManagerSettings::default())
.expect("Error to load AudioManager"); .expect("Error to load AudioManager");
//let font_bytes = include_bytes!("../fonts/");
( (
Self { Self {
time_last_frame: Instant::now(), time_last_frame: Instant::now(),
@@ -86,6 +90,7 @@ impl MyApp {
music: Music::default(), music: Music::default(),
current_delta: 0.0, current_delta: 0.0,
str_music_length: "01:00:00".to_string(), str_music_length: "01:00:00".to_string(),
str_time: "00:00:00".to_string(),
}, },
Task::none(), Task::none(),
) )
@@ -112,17 +117,16 @@ impl MyApp {
self.music self.music
.apply_tick(self.current_delta, time_btw, &mut self.audio_manager); .apply_tick(self.current_delta, time_btw, &mut self.audio_manager);
self.time_last_frame = Instant::now(); self.time_last_frame = Instant::now();
self.str_time = delta_to_string(self.current_delta);
} }
} }
Message::Remove(i) => { Message::Remove(i) => {
self.music.remove_polygon(self.current_delta, i - 1); self.music.remove_polygon(self.current_delta, i);
} }
Message::ChangeTeta(i, teta) => { Message::ChangeTeta(i, teta) => {
self.music.set_teta(self.current_delta, i, teta); self.music.set_teta(self.current_delta, i, teta);
} }
Message::ChangeColor(i, s) => {
self.music.set_color(self.current_delta, i, s);
}
Message::ChangeSound(i, s) => { Message::ChangeSound(i, s) => {
let sound = StaticSoundData::from_file(format!("./assets/{s}")) let sound = StaticSoundData::from_file(format!("./assets/{s}"))
.expect("Fail to load audio"); .expect("Fail to load audio");
@@ -148,41 +152,99 @@ impl MyApp {
} }
Message::ToggleSavePanel => self.show_save_panel = !self.show_save_panel, Message::ToggleSavePanel => self.show_save_panel = !self.show_save_panel,
Message::FileNameChanged(s) => self.music.file_name = s, Message::FileNameChanged(s) => self.music.file_name = s,
Message::LengthChange(s) => self.str_music_length = s, Message::LengthChange(s) => {
if is_delta_format_valid(&s) {
let sec = str_to_sec(&s);
if sec > 0. {
self.music.length = sec;
}
}
self.str_music_length = s;
}
Message::TogglePaused => { Message::TogglePaused => {
self.paused = !self.paused; self.paused = !self.paused;
if !self.paused { if !self.paused {
self.time_last_frame = Instant::now(); self.time_last_frame = Instant::now();
} }
} }
Message::SetMusicLength => {
if self.is_length_valid() {
self.music.length = str_to_sec(&self.str_music_length)
}
}
Message::ChangeDelta(f) => { Message::ChangeDelta(f) => {
self.current_delta = f; self.current_delta = f;
self.music.fix_teta(self.current_delta); self.music.fix_teta(self.current_delta);
// update the red dot on canvas // update the red dot on canvas
if self.paused { self.update_canvas_if_paused();
self.update(Message::TogglePaused); }
self.update(Message::Tick); Message::ChangeDeltaString(s) => {
self.update(Message::TogglePaused); if is_delta_format_valid(&s) {
self.update(Message::ChangeDelta(str_to_sec(&s)));
} }
self.str_time = s;
} }
Message::AddPoint => { Message::AddPoint => {
self.music.add_point(self.current_delta); self.music.add_point(self.current_delta);
} }
Message::RemovePoint => {
self.music.remove_point(self.current_delta);
}
Message::ClickedOnTimeLine(f) => {
self.update(Message::ChangeDelta(f));
}
Message::SlidePointLeft => {
self.music.slide_to_left(self.current_delta);
self.music.fix_teta(self.current_delta);
self.update_canvas_if_paused();
}
Message::SlidePointRight => {
self.music.slide_to_right(self.current_delta);
self.music.fix_teta(self.current_delta);
self.update_canvas_if_paused();
}
Message::ChangeDegree(i, s) => {
let mut mut_s = s;
if mut_s.len() == 0 {
mut_s = "0".to_string();
}
match mut_s.parse::<f32>() {
Ok(val) => {
if val <= 360. {
self.update(Message::ChangeTeta(i, (val).to_radians()))
}
}
Err(_) => {}
}
}
Message::ChangeNbPerSec(s) => {
let mut_s = s.trim_end_matches(" rev/sec");
if mut_s.len() != s.len() {
match mut_s.parse::<f32>() {
Ok(val) => {
let val = (val * 10.).floor() / 10.;
if val >= 1. {
self.music.nb_sec_for_rev = val
}
}
Err(_) => {}
}
}
}
Message::CancelColor(i) => {
self.music.set_color_picker(self.current_delta, i, false);
self.paused = false;
}
Message::SubmitColor(i) => {
if !self.paused {
self.update(Message::TogglePaused);
}
self.music.set_color_picker(self.current_delta, i, true);
}
Message::ChooseColor(i, color) => {
self.music.set_color(self.current_delta, i, color);
self.music.set_color_picker(self.current_delta, i, false);
}
} }
} }
fn view(&self) -> iced::Element<Message> { fn view(&self) -> iced::Element<Message> {
let txt_nb_rev = if self.music.nb_sec_for_rev % 1. != 0.0 {
format!("{} sec/revolution", self.music.nb_sec_for_rev)
} else {
format!("{}.0 sec/revolution", self.music.nb_sec_for_rev)
};
let mut i = 0; let mut i = 0;
let entries = self.all_sounds.clone(); let entries = self.all_sounds.clone();
//Create all polygon options //Create all polygon options
@@ -193,30 +255,44 @@ impl MyApp {
.iter() .iter()
.map(|polygon| { .map(|polygon| {
let current_index = i; let current_index = i;
i += 1; let but = button(text("").size(20).center()).on_press(Message::SubmitColor(i));
column![ let c = column![
row![ row![
text(&polygon.name), text(&polygon.name).size(24),
button("Remove").on_press(Message::Remove(i)), button(text("").size(20)).on_press(Message::Remove(i)),
pick_list( color_picker(
["Black", "Blue", "Green", "Pink", "Yellow", "Cyan"] polygon.show_color_picker,
.map(|s| s.to_string()) polygon.color,
.to_vec(), but,
Some(&polygon.color_name), Message::CancelColor(i),
move |s| { Message::ChangeColor(current_index, s) } move |color| Message::ChooseColor(i, color)
), ),
pick_list(entries.clone(), Some(&polygon.sound_name), move |s| { pick_list(entries.clone(), Some(&polygon.sound_name), move |s| {
Message::ChangeSound(current_index, s) Message::ChangeSound(current_index, s)
}), })
.text_size(20),
] ]
.spacing(20), .spacing(20),
slider(0.0..=2.0 * PI, polygon.global_teta, move |f| { row![
Message::ChangeTeta(current_index, f) TextInput::new("90", &polygon.global_teta.to_degrees().floor().to_string())
}) .on_input(move |new_value| Message::ChangeDegree(
.step(PI / 84f32), // 84 | 4 for do PI / 4 current_index,
new_value
))
.width(Length::FillPortion(1)),
slider(0.0..=2.0 * PI, polygon.global_teta, move |f| {
Message::ChangeTeta(current_index, f)
})
.step(PI / 84f32) // 84 | 4 for do PI / 4
.width(Length::FillPortion(9))
.height(32),
]
.spacing(10),
] ]
.spacing(10) .spacing(10)
.into() .into();
i += 1;
c
}) })
.collect(); .collect();
let ngon_options: Vec<String> = (5..=42).map(|sides| format!("Ngon{sides}")).collect(); let ngon_options: Vec<String> = (5..=42).map(|sides| format!("Ngon{sides}")).collect();
@@ -264,29 +340,8 @@ impl MyApp {
save_panel.push(button("Load").on_press(Message::Load).into()); save_panel.push(button("Load").on_press(Message::Load).into());
} }
column![ column![
text(&self.music.file_name).size(32.0), text("Polymusic").size(32.0),
row(save_panel).spacing(20), row(save_panel).spacing(20),
row![
text("Music Length").size(20),
TextInput::new("MM:SS:CS", &self.str_music_length)
.on_input(|new_value| Message::LengthChange(new_value))
.icon(Icon {
font: Font::DEFAULT,
code_point: if self.is_length_valid() {
'\u{2705}'
} else {
'\u{274C}'
},
size: None,
spacing: 10.,
side: Side::Left,
}),
button("Valid").on_press(Message::SetMusicLength),
text(txt_nb_rev).size(20),
button("Increment").on_press(Message::ButtonPressedIncrement),
button("Decrement").on_press(Message::ButtonPressedDecrement),
]
.spacing(20),
row![ row![
container( container(
canvas(self.music.current_frame(self.current_delta)) canvas(self.music.current_frame(self.current_delta))
@@ -294,10 +349,11 @@ impl MyApp {
.width(Length::FillPortion(1)) .width(Length::FillPortion(1))
), ),
column![ column![
text("Polygon options"), text("Polygon options").size(26),
pick_list(all_options, Some("Choose polygon".to_string()), |s| { pick_list(all_options, Some("Choose polygon".to_string()), |s| {
Message::AddPolygon(s) Message::AddPolygon(s)
}), })
.text_size(18),
polygon_column, polygon_column,
] ]
.spacing(10) .spacing(10)
@@ -308,27 +364,50 @@ impl MyApp {
.spacing(20), .spacing(20),
column![ column![
row![ row![
button("Toggle Play").on_press(Message::TogglePaused), button(text(if self.paused { "󰐊" } else { "󰏤" }).size(28).center())
text(format!( .on_press(Message::TogglePaused)
"{}/{}", .width(Length::FillPortion(1)),
delta_to_string(self.current_delta), row![
delta_to_string(self.music.length) TextInput::new("MM:SS:CS", &self.str_time)
)) .on_input(|new_value| Message::ChangeDeltaString(new_value))
.size(20.0), .size(28),
button("Add Point").on_press(Message::AddPoint) text("/").size(30),
TextInput::new("MM:SS:CS", &self.str_music_length)
.on_input(|new_value| Message::LengthChange(new_value))
.size(28),
]
.width(Length::FillPortion(10)),
TextInput::new("1.0", &format!("{:.1} rev/sec", &self.music.nb_sec_for_rev))
.on_input(|new_value| Message::ChangeNbPerSec(new_value))
.size(28)
.width(Length::FillPortion(2)),
button(text("").size(28).center())
.on_press(Message::SlidePointLeft)
.width(Length::FillPortion(1)),
button(text("").size(28).center())
.on_press(Message::AddPoint)
.width(Length::FillPortion(1)),
button(text("").size(28).center())
.on_press(Message::SlidePointRight)
.width(Length::FillPortion(1)),
button(text("").size(28).center())
.on_press(Message::RemovePoint)
.width(Length::FillPortion(1)),
] ]
.spacing(20), .spacing(20),
column![ column![
/*
slider(0.0..=self.music.length, self.current_delta, move |f| { slider(0.0..=self.music.length, self.current_delta, move |f| {
Message::ChangeDelta(f) Message::ChangeDelta(f)
}) })
.step(&self.music.length / 10_000.), .step(&self.music.length / 10_000.),*/
canvas(&self.music) canvas(&self.music)
.height(Length::FillPortion(1)) .height(Length::FillPortion(1))
.width(Length::FillPortion(1)) .width(Length::FillPortion(1))
] ]
.spacing(0), .spacing(0),
] ]
.spacing(20)
.height(Length::FillPortion(1)) .height(Length::FillPortion(1))
.width(Length::FillPortion(1)), .width(Length::FillPortion(1)),
] ]
@@ -337,15 +416,20 @@ impl MyApp {
.into() .into()
} }
fn is_length_valid(&self) -> bool {
let re = Regex::new(r"^\d{2}:\d{2}:\d{2}$").unwrap();
re.is_match(self.str_music_length.as_str())
}
fn subscription(&self) -> iced::Subscription<Message> { fn subscription(&self) -> iced::Subscription<Message> {
iced::Subscription::batch([ if self.paused {
//window::events().map(|(_id, event)| Message::WindowEvent(event)), Subscription::none() // ➝ désactive toutes les subscriptions
iced::time::every(std::time::Duration::from_millis(16)).map(|_| Message::Tick), } else {
]) iced::time::every(std::time::Duration::from_millis(16)).map(|_| Message::Tick)
}
}
fn update_canvas_if_paused(&mut self) {
if self.paused {
self.update(Message::TogglePaused);
self.update(Message::Tick);
self.update(Message::TogglePaused);
}
} }
} }

29
src/message.rs Normal file
View File

@@ -0,0 +1,29 @@
use iced::Color;
#[derive(Debug, Clone)]
pub enum Message {
ChangeNbPerSec(String),
Tick,
AddPolygon(String),
ChangeTeta(usize, f32),
Remove(usize),
ChangeSound(usize, String),
ToggleSavePanel,
Save,
Load,
FileNameChanged(String),
TogglePaused,
LengthChange(String),
ChangeDelta(f32),
AddPoint,
RemovePoint,
ClickedOnTimeLine(f32),
ChangeDeltaString(String),
SlidePointLeft,
SlidePointRight,
ChangeDegree(usize, String),
ChooseColor(usize, Color),
CancelColor(usize),
SubmitColor(usize),
}

View File

@@ -1,15 +1,22 @@
use crate::message::Message;
use crate::utils::string_to_polygon; use crate::utils::string_to_polygon;
use crate::{polygon_draw::*, utils::string_to_color}; use crate::polygon_draw::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use kira::{AudioManager, sound::static_sound::StaticSoundData}; use kira::{AudioManager, sound::static_sound::StaticSoundData};
use iced::mouse; use iced::Vector;
use iced::event::Status;
use iced::mouse::Cursor;
use iced::widget::canvas::{Event, Geometry};
use iced::{Size, mouse};
use iced::widget::canvas; use iced::widget::canvas;
use iced::widget::canvas::Stroke; use iced::widget::canvas::Stroke;
use iced::widget::canvas::Style; use iced::widget::canvas::Style;
use iced::{Color, Rectangle, Renderer, Theme}; use iced::{Color, Rectangle, Renderer, Theme};
use std::f32::consts::PI; use std::f32::consts::PI;
use std::mem::swap;
use std::time::Duration; use std::time::Duration;
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct Music { pub struct Music {
@@ -35,6 +42,17 @@ impl Music {
&mut self.poly_frame.last_mut().unwrap().1 &mut self.poly_frame.last_mut().unwrap().1
} }
} }
fn find_index_frame(&mut self, delta: f32) -> usize {
if let Some(i) = self
.poly_frame
.windows(2)
.position(|w| w[0].0 <= delta && delta < w[1].0)
{
i
} else {
self.poly_frame.len() - 1
}
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~PUBLIC~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~PUBLIC~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
pub fn current_frame(&self, delta: f32) -> &PolygonFrame { pub fn current_frame(&self, delta: f32) -> &PolygonFrame {
if let Some(i) = self if let Some(i) = self
@@ -105,10 +123,14 @@ impl Music {
self.find_poly_frame(delta).polygons[index].global_teta = teta; self.find_poly_frame(delta).polygons[index].global_teta = teta;
} }
pub fn set_color(&mut self, delta: f32, index: usize, color_name: String) { pub fn set_color(&mut self, delta: f32, index: usize, color: Color) {
let current_frame = self.find_poly_frame(delta); let current_frame = self.find_poly_frame(delta);
current_frame.polygons[index].color = string_to_color(&color_name); current_frame.polygons[index].color = color;
current_frame.polygons[index].color_name = color_name; }
pub fn set_color_picker(&mut self, delta: f32, i: usize, b: bool) {
let current_frame = self.find_poly_frame(delta);
current_frame.polygons[i].show_color_picker = b;
} }
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ADD/REMOVE~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ADD/REMOVE~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -121,6 +143,12 @@ impl Music {
self.poly_frame self.poly_frame
.insert(pos, (delta, self.current_frame(delta).clone())); .insert(pos, (delta, self.current_frame(delta).clone()));
} }
pub fn remove_point(&mut self, delta: f32) {
let i = self.find_index_frame(delta);
if i != 0 {
self.poly_frame.remove(i);
}
}
pub fn add_polygon(&mut self, delta: f32, polygon_name: String) { pub fn add_polygon(&mut self, delta: f32, polygon_name: String) {
let current_frame = self.find_poly_frame(delta); let current_frame = self.find_poly_frame(delta);
@@ -131,47 +159,86 @@ impl Music {
pub fn remove_polygon(&mut self, delta: f32, i: usize) { pub fn remove_polygon(&mut self, delta: f32, i: usize) {
self.find_poly_frame(delta).polygons.remove(i); self.find_poly_frame(delta).polygons.remove(i);
} }
pub fn slide_to_left(&mut self, delta: f32) {
let i = self.find_index_frame(delta);
if i > 0 {
let (left, right) = self.poly_frame.split_at_mut(i);
swap(&mut left[i - 1].1, &mut right[0].1);
}
}
pub fn slide_to_right(&mut self, delta: f32) {
let i = self.find_index_frame(delta);
if i < self.poly_frame.len() - 1 {
let (left, right) = self.poly_frame.split_at_mut(i + 1);
swap(&mut left[i].1, &mut right[0].1);
}
}
} }
impl<Message> canvas::Program<Message> for Music { impl canvas::Program<Message> for Music {
// No internal state // No internal state
type State = (); type State = bool;
fn draw( fn draw(
&self, &self,
_state: &(), _state: &Self::State,
renderer: &Renderer, renderer: &Renderer,
_theme: &Theme, _theme: &Theme,
bounds: Rectangle, bounds: Rectangle,
_cursor: mouse::Cursor, _cursor: mouse::Cursor,
) -> Vec<canvas::Geometry> { ) -> Vec<canvas::Geometry> {
let mut frame = canvas::Frame::new(renderer, bounds.size()); let mut geo_small_frame: Vec<Geometry> = vec![];
let mut geo_cursor: Vec<Geometry> = vec![];
let frame = canvas::Frame::new(renderer, bounds.size());
let mut toggle_color = true; let mut toggle_color = true;
let padding = 8.; let padding = 8.;
let w = bounds.width - (padding * 2.); let w = bounds.width - (padding * 2.);
for (delta, _) in &self.poly_frame { for (delta, polyframe) in &self.poly_frame {
let x = delta / self.length * w + padding; let x = delta / self.length * w + 8.;
frame.fill_rectangle( let mut back_frame = canvas::Frame::new(
renderer,
Size {
width: bounds.width,
height: bounds.height,
},
);
back_frame.fill_rectangle(
iced::Point { x: x, y: 0.0 }, iced::Point { x: x, y: 0.0 },
frame.size(), frame.size(),
if toggle_color { if toggle_color {
Color::from_rgb(0.3, 0.3, 0.3) Color::from_rgb8(27, 60, 83)
} else { } else {
Color::from_rgb(0.1, 0.1, 0.1) Color::from_rgb8(69, 104, 130)
}, },
); );
geo_small_frame.push(back_frame.into_geometry());
toggle_color = !toggle_color; toggle_color = !toggle_color;
} let mut small_frame = canvas::Frame::new(
frame.stroke_rectangle( renderer,
iced::Point { x: 0.0, y: 0.0 }, Size {
frame.size(), width: bounds.width,
Stroke { height: bounds.height,
width: 16.0, },
..Stroke::default() );
},
);
let x = self.current_delta / self.length * w + padding; small_frame.translate(Vector {
frame.stroke_rectangle( x: x + (bounds.height / 10.),
y: (bounds.height / 10.),
});
polyframe.draw_in_frame(
&mut small_frame,
Size {
width: (8. * bounds.height) / 10.,
height: (8. * bounds.height) / 10.,
},
);
geo_small_frame.push(small_frame.into_geometry());
}
let x = self.current_delta / self.length * w + 8.;
let mut frame_cursor = canvas::Frame::new(renderer, bounds.size());
frame_cursor.stroke_rectangle(
iced::Point::new(x, 0.), iced::Point::new(x, 0.),
iced::Size { iced::Size {
width: 0., width: 0.,
@@ -179,12 +246,63 @@ impl<Message> canvas::Program<Message> for Music {
}, },
Stroke { Stroke {
width: 4.0, width: 4.0,
style: Style::Solid(Color::from_rgb(1.0, 0.0, 0.0)), style: Style::Solid(Color::from_rgba(1.0, 0.0, 0.0, 1.)),
..Stroke::default() ..Stroke::default()
}, },
); );
frame_cursor.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()
},
);
geo_cursor.push(frame_cursor.into_geometry());
//vec_geo.push(frame.into_geometry());
// Then, we produce the geometry // Then, we produce the geometry
vec![frame.into_geometry()] let mut out = vec![frame.into_geometry()];
out.append(&mut geo_small_frame);
out.append(&mut geo_cursor);
out
}
fn update(
&self,
state: &mut Self::State,
event: Event,
bounds: Rectangle,
cursor: Cursor,
) -> (Status, Option<Message>) {
//eprintln!("event = {:?}", event);
if let Event::Mouse(mouse_event) = event {
match mouse_event {
mouse::Event::ButtonPressed(mouse::Button::Left) => {
*state = true;
if let Some(position) = cursor.position_in(bounds) {
let pos_x = (position.x - 8.0) / (bounds.width - 16.);
let delta = (pos_x * self.length).clamp(0., self.length);
return (
Status::Captured,
Some(crate::message::Message::ClickedOnTimeLine(delta)),
);
}
}
mouse::Event::ButtonReleased(mouse::Button::Left) => *state = false,
mouse::Event::CursorMoved { position: _ } => {
if let Some(position) = cursor.position_in(bounds)
&& *state
{
let pos_x = (position.x - 8.0) / (bounds.width - 16.);
let delta = (pos_x * self.length).clamp(0., self.length);
return (Status::Captured, Some(Message::ClickedOnTimeLine(delta)));
}
}
_ => {}
}
}
(Status::Ignored, None)
} }
} }

View File

@@ -2,12 +2,12 @@ use crate::utils::string_to_color;
use std::f32::consts::PI; use std::f32::consts::PI;
use iced::Size;
use iced::Vector; use iced::Vector;
use iced::mouse; use iced::mouse;
use iced::widget::canvas; use iced::widget::canvas;
use iced::widget::canvas::Stroke; use iced::widget::canvas::{Frame, Stroke, Style};
use iced::widget::canvas::Style; use iced::{Color, Point, Rectangle, Renderer, Theme};
use iced::{Color, Rectangle, Renderer, Theme};
use kira::sound::static_sound::StaticSoundData; use kira::sound::static_sound::StaticSoundData;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@@ -49,6 +49,65 @@ impl PolygonFrame {
polygons: vec![], 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 { impl<Message> canvas::Program<Message> for PolygonFrame {
@@ -67,13 +126,18 @@ impl<Message> canvas::Program<Message> for PolygonFrame {
let radius = frame.size().width.min(frame.size().height) / 2.0 - 32.0; let radius = frame.size().width.min(frame.size().height) / 2.0 - 32.0;
let mut vec = Vector::new(0.0, -radius); let mut vec = Vector::new(0.0, -radius);
let c = frame.center(); let c = frame.center();
frame.fill_rectangle(
iced::Point { x: 0., y: 0. },
frame.size(),
Color::from_rgb8(27, 60, 83),
);
// Draw Circle // Draw Circle
let circle = canvas::Path::circle(frame.center(), radius); let circle = canvas::Path::circle(frame.center(), radius);
frame.stroke( frame.stroke(
&circle, &circle,
Stroke { Stroke {
width: 6.0, width: 6.0,
style: Style::Solid(Color::from_rgba(0.0, 0.0, 0.0, 1.0)), style: Style::Solid(Color::from_rgb8(210, 193, 182)),
..Stroke::default() ..Stroke::default()
}, },
); );
@@ -100,7 +164,7 @@ impl<Message> canvas::Program<Message> for PolygonFrame {
} }
// Draw the red dot on the current position // Draw the red dot on the current position
let dot = canvas::Path::circle(frame.center() + vec.rotate(self.teta), 5.0); 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)); frame.fill(&dot, Color::from_rgb(1.0, 0.0, 0.0));
// Draw the frame // Draw the frame
@@ -109,6 +173,7 @@ impl<Message> canvas::Program<Message> for PolygonFrame {
frame.size(), frame.size(),
Stroke { Stroke {
width: 16.0, width: 16.0,
style: Style::Solid(Color::from_rgb8(207, 74, 28)),
..Stroke::default() ..Stroke::default()
}, },
); );
@@ -129,6 +194,7 @@ pub struct Polygon {
#[serde(skip)] #[serde(skip)]
pub color: Color, pub color: Color,
pub color_name: String, pub color_name: String,
pub show_color_picker: bool,
} }
#[warn(dead_code)] #[warn(dead_code)]
impl Polygon { impl Polygon {
@@ -153,196 +219,154 @@ impl Polygon {
} }
sound_to_play sound_to_play
} }
pub fn default(sound: StaticSoundData) -> Self {
Polygon {
global_teta: 0.,
points_teta: vec![],
sound: sound,
sound_name: "tick.ogg".to_string(),
color_name: "Black".to_string(),
name: "".to_string(),
color: Color::BLACK,
show_color_picker: false,
}
}
pub fn n_gon(teta: f32, n_side: u8, sound: StaticSoundData) -> Self { pub fn n_gon(n_side: u8, sound: StaticSoundData) -> Self {
let mut v: Vec<f32> = Vec::with_capacity(n_side as usize); let mut v: Vec<f32> = Vec::with_capacity(n_side as usize);
for i in 0..n_side { for i in 0..n_side {
v.push((i as f32 * 2.0 * PI) / n_side as f32); v.push((i as f32 * 2.0 * PI) / n_side as f32);
} }
Polygon { let mut p = Polygon::default(sound);
global_teta: teta, p.points_teta = v;
points_teta: v, p
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 { pub fn segment(sound: StaticSoundData) -> Self {
Polygon::n_gon(teta, 2, sound) Polygon::n_gon(2, sound)
} }
pub fn triangle(teta: f32, sound: StaticSoundData) -> Self { pub fn triangle(sound: StaticSoundData) -> Self {
Polygon::n_gon(teta, 3, sound) Polygon::n_gon(3, sound)
} }
pub fn square(teta: f32, sound: StaticSoundData) -> Self { pub fn square(sound: StaticSoundData) -> Self {
Polygon::n_gon(teta, 4, sound) Polygon::n_gon(4, sound)
} }
pub fn nr_6_in_30(teta: f32, sound: StaticSoundData) -> Self { pub fn nr_6_in_30(sound: StaticSoundData) -> Self {
Polygon { let mut p = Polygon::default(sound);
sound: sound, p.points_teta = vec![
name: "".to_string(), 2.0 * 5.0 * PI / 30.0,
color: Color::BLACK, 2.0 * 6.0 * PI / 30.0,
global_teta: teta, 2.0 * 12.0 * PI / 30.0,
sound_name: "tick.ogg".to_string(), 2.0 * 18.0 * PI / 30.0,
color_name: "Black".to_string(), 2.0 * 24.0 * PI / 30.0,
points_teta: vec![ 2.0 * 25.0 * PI / 30.0,
2.0 * 5.0 * PI / 30.0, ];
2.0 * 6.0 * PI / 30.0, p
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 { pub fn nr_7_in_30(sound: StaticSoundData) -> Self {
Polygon { let mut p = Polygon::default(sound);
sound: sound, p.points_teta = vec![
name: "".to_string(), 0.0,
color: Color::BLACK, 2.0 * 6.0 * PI / 30.0,
sound_name: "tick.ogg".to_string(), 2.0 * 7.0 * PI / 30.0,
color_name: "Black".to_string(), 2.0 * 13.0 * PI / 30.0,
global_teta: teta, 2.0 * 17.0 * PI / 30.0,
points_teta: vec![ 2.0 * 23.0 * PI / 30.0,
0.0, 2.0 * 24.0 * PI / 30.0,
2.0 * 6.0 * PI / 30.0, ];
2.0 * 7.0 * PI / 30.0, p
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 { pub fn nr_8_in_30(sound: StaticSoundData) -> Self {
Polygon { let mut p = Polygon::default(sound);
sound: sound, p.points_teta = vec![
name: "".to_string(), 2.0 * PI / 30.0,
color: Color::BLACK, 2.0 * 5.0 * PI / 30.0,
sound_name: "tick.ogg".to_string(), 2.0 * 11.0 * PI / 30.0,
color_name: "Black".to_string(), 2.0 * 12.0 * PI / 30.0,
global_teta: teta, 2.0 * 18.0 * PI / 30.0,
points_teta: vec![ 2.0 * 19.0 * PI / 30.0,
2.0 * PI / 30.0, 2.0 * 25.0 * PI / 30.0,
2.0 * 5.0 * PI / 30.0, 2.0 * 29.0 * PI / 30.0,
2.0 * 11.0 * PI / 30.0, ];
2.0 * 12.0 * PI / 30.0, p
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 { pub fn nr_9_in_30(sound: StaticSoundData) -> Self {
Polygon { let mut p = Polygon::default(sound);
sound: sound, p.points_teta = vec![
name: "".to_string(), 0.0,
color: Color::BLACK, 2.0 * PI / 30.0,
sound_name: "tick.ogg".to_string(), 2.0 * 7.0 * PI / 30.0,
color_name: "Black".to_string(), 2.0 * 11.0 * PI / 30.0,
global_teta: teta, 2.0 * 13.0 * PI / 30.0,
points_teta: vec![ 2.0 * 17.0 * PI / 30.0,
0.0, 2.0 * 19.0 * PI / 30.0,
2.0 * PI / 30.0, 2.0 * 23.0 * PI / 30.0,
2.0 * 7.0 * PI / 30.0, 2.0 * 29.0 * PI / 30.0,
2.0 * 11.0 * PI / 30.0, ];
2.0 * 13.0 * PI / 30.0, p
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 { pub fn nr_8_in_42(sound: StaticSoundData) -> Self {
Polygon { let mut p = Polygon::default(sound);
sound: sound, p.points_teta = vec![
name: "".to_string(), 2.0 * 3.0 * PI / 42.0,
color: Color::BLACK, 2.0 * 9.0 * PI / 42.0,
sound_name: "tick.ogg".to_string(), 2.0 * 14.0 * PI / 42.0,
color_name: "Black".to_string(), 2.0 * 15.0 * PI / 42.0,
global_teta: teta, 2.0 * 27.0 * PI / 42.0,
points_teta: vec![ 2.0 * 28.0 * PI / 42.0,
2.0 * 3.0 * PI / 42.0, 2.0 * 33.0 * PI / 42.0,
2.0 * 9.0 * PI / 42.0, 2.0 * 39.0 * PI / 42.0,
2.0 * 14.0 * PI / 42.0, ];
2.0 * 15.0 * PI / 42.0, p
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 { pub fn nr_9_in_42(sound: StaticSoundData) -> Self {
Polygon { let mut p = Polygon::default(sound);
sound: sound, p.points_teta = vec![
name: "".to_string(), 0.0,
color: Color::BLACK, 2.0 * 6.0 * PI / 42.0,
sound_name: "tick.ogg".to_string(), 2.0 * 11.0 * PI / 42.0,
color_name: "Black".to_string(), 2.0 * 12.0 * PI / 42.0,
global_teta: teta, 2.0 * 17.0 * PI / 42.0,
points_teta: vec![ 2.0 * 25.0 * PI / 42.0,
0.0, 2.0 * 30.0 * PI / 42.0,
2.0 * 6.0 * PI / 42.0, 2.0 * 31.0 * PI / 42.0,
2.0 * 11.0 * PI / 42.0, 2.0 * 36.0 * PI / 42.0,
2.0 * 12.0 * PI / 42.0, ];
2.0 * 17.0 * PI / 42.0, p
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 { pub fn nr_10a_in_42(sound: StaticSoundData) -> Self {
Polygon { let mut p = Polygon::default(sound);
sound: sound, p.points_teta = vec![
name: "".to_string(), 2.0 * 6.0 * PI / 42.0,
color: Color::BLACK, 2.0 * 7.0 * PI / 42.0,
sound_name: "tick.ogg".to_string(), 2.0 * 11.0 * PI / 42.0,
color_name: "Black".to_string(), 2.0 * 12.0 * PI / 42.0,
global_teta: teta, 2.0 * 17.0 * PI / 42.0,
points_teta: vec![ 2.0 * 25.0 * PI / 42.0,
2.0 * 6.0 * PI / 42.0, 2.0 * 30.0 * PI / 42.0,
2.0 * 7.0 * PI / 42.0, 2.0 * 31.0 * PI / 42.0,
2.0 * 11.0 * PI / 42.0, 2.0 * 35.0 * PI / 42.0,
2.0 * 12.0 * PI / 42.0, 2.0 * 36.0 * PI / 42.0,
2.0 * 17.0 * PI / 42.0, ];
2.0 * 25.0 * PI / 42.0, p
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 { pub fn nr_10b_in_42(sound: StaticSoundData) -> Self {
Polygon { let mut p = Polygon::default(sound);
sound: sound, p.points_teta = vec![
name: "".to_string(), 0.0,
color: Color::BLACK, 2.0 * 1.0 * PI / 42.0,
sound_name: "tick.ogg".to_string(), 2.0 * 5.0 * PI / 42.0,
color_name: "Black".to_string(), 2.0 * 13.0 * PI / 42.0,
global_teta: teta, 2.0 * 18.0 * PI / 42.0,
points_teta: vec![ 2.0 * 19.0 * PI / 42.0,
0.0, 2.0 * 24.0 * PI / 42.0,
2.0 * 1.0 * PI / 42.0, 2.0 * 29.0 * PI / 42.0,
2.0 * 5.0 * PI / 42.0, 2.0 * 30.0 * PI / 42.0,
2.0 * 13.0 * PI / 42.0, 2.0 * 41.0 * PI / 42.0,
2.0 * 18.0 * PI / 42.0, ];
2.0 * 19.0 * PI / 42.0, p
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 { fn dummy_sound() -> StaticSoundData {

View File

@@ -1,6 +1,12 @@
use crate::Polygon; use crate::Polygon;
use iced::Color; use iced::Color;
use kira::sound::static_sound::StaticSoundData; use kira::sound::static_sound::StaticSoundData;
use regex::Regex;
pub fn is_delta_format_valid(str: &str) -> bool {
let re = Regex::new(r"^\d{1,2}:\d{1,2}:\d{1,2}$").unwrap();
re.is_match(str)
}
pub fn string_to_color<S: AsRef<str>>(s: S) -> Color { pub fn string_to_color<S: AsRef<str>>(s: S) -> Color {
match s.as_ref() { match s.as_ref() {
"Green" => Color::from_rgb(0.0, 1.0, 0.0), "Green" => Color::from_rgb(0.0, 1.0, 0.0),
@@ -43,46 +49,46 @@ pub fn string_to_polygon<S: AsRef<str>>(str: S) -> Polygon {
let mut poly: Polygon; let mut poly: Polygon;
if s.starts_with("Ngon") { if s.starts_with("Ngon") {
if let Ok(sides) = s.trim_start_matches("Ngon").parse::<u8>() { if let Ok(sides) = s.trim_start_matches("Ngon").parse::<u8>() {
poly = Polygon::n_gon(0.0, sides, dummy_sound()); poly = Polygon::n_gon(sides, dummy_sound());
} else { } else {
poly = Polygon::n_gon(0.0, 0, dummy_sound()); poly = Polygon::n_gon(0, dummy_sound());
} }
} else { } else {
match s { match s {
"Segment" => { "Segment" => {
poly = Polygon::segment(0.0, dummy_sound()); poly = Polygon::segment(dummy_sound());
} }
"Triangle" => { "Triangle" => {
poly = Polygon::triangle(0.0, dummy_sound()); poly = Polygon::triangle(dummy_sound());
} }
"Square" => { "Square" => {
poly = Polygon::square(0.0, dummy_sound()); poly = Polygon::square(dummy_sound());
} }
"Nr6In30" => { "Nr6In30" => {
poly = Polygon::nr_6_in_30(0.0, dummy_sound()); poly = Polygon::nr_6_in_30(dummy_sound());
} }
"Nr7In30" => { "Nr7In30" => {
poly = Polygon::nr_7_in_30(0.0, dummy_sound()); poly = Polygon::nr_7_in_30(dummy_sound());
} }
"Nr8In30" => { "Nr8In30" => {
poly = Polygon::nr_8_in_30(0.0, dummy_sound()); poly = Polygon::nr_8_in_30(dummy_sound());
} }
"Nr9In30" => { "Nr9In30" => {
poly = Polygon::nr_9_in_30(0.0, dummy_sound()); poly = Polygon::nr_9_in_30(dummy_sound());
} }
"Nr8In42" => { "Nr8In42" => {
poly = Polygon::nr_8_in_42(0.0, dummy_sound()); poly = Polygon::nr_8_in_42(dummy_sound());
} }
"Nr9In42" => { "Nr9In42" => {
poly = Polygon::nr_9_in_42(0.0, dummy_sound()); poly = Polygon::nr_9_in_42(dummy_sound());
} }
"Nr10aIn42" => { "Nr10aIn42" => {
poly = Polygon::nr_10a_in_42(0.0, dummy_sound()); poly = Polygon::nr_10a_in_42(dummy_sound());
} }
"Nr10bIn42" => { "Nr10bIn42" => {
poly = Polygon::nr_10b_in_42(0.0, dummy_sound()); poly = Polygon::nr_10b_in_42(dummy_sound());
} }
_ => poly = Polygon::n_gon(0.0, 0, dummy_sound()), _ => poly = Polygon::n_gon(0, dummy_sound()),
} }
} }
poly.name = s.to_string(); poly.name = s.to_string();