From a7b6e2acd62c7143400333c2a36ed2be66d651c8 Mon Sep 17 00:00:00 2001 From: Dukantic Date: Tue, 8 Jul 2025 21:55:05 +0200 Subject: [PATCH] vizualizer of different polygon frame --- src/main.rs | 103 ++++++++++++++++++++++++++++++++++----------------- src/music.rs | 75 +++++++++++++++++++++++++++++++++++-- 2 files changed, 141 insertions(+), 37 deletions(-) diff --git a/src/main.rs b/src/main.rs index 5d0920b..d8ab1bf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,12 +12,17 @@ use std::fs; use iced::{Element, window}; use iced::{ - Length, Task, Theme, + Font, + font::Family, widget::{ - Column, TextInput, button, canvas, column, container, pick_list, row, scrollable, slider, - text, + TextInput, column, text, text_input, + text_input::{Icon, Side}, }, }; +use iced::{ + Length, Task, Theme, + widget::{Column, button, canvas, container, pick_list, row, scrollable, slider}, +}; use regex::Regex; use std::f32::consts::PI; @@ -37,7 +42,6 @@ fn main() -> iced::Result { #[derive(Debug, Clone)] enum Message { - WindowEvent(window::Event), ButtonPressedIncrement, ButtonPressedDecrement, Tick, @@ -89,9 +93,6 @@ impl MyApp { } fn update(&mut self, message: Message) { match message { - Message::WindowEvent(window::Event::Resized(size)) => { - println!("Resize detected: {}x{}", size.width, size.height); - } Message::ButtonPressedIncrement => self.music.nb_sec_for_rev += 0.5, Message::ButtonPressedDecrement => { if self.music.nb_sec_for_rev > 0.5 { @@ -102,7 +103,11 @@ impl MyApp { self.music.add_polygon(self.current_delta, s); } Message::Tick => { + if self.current_delta >= self.music.length { + self.paused = true + } if !self.paused { + self.music.current_delta = self.current_delta; let time_btw = Instant::now().duration_since(self.time_last_frame); self.current_delta += time_btw.as_millis() as f32 / 1000.0; self.music @@ -126,10 +131,11 @@ impl MyApp { } Message::Save => { let json = serde_json::to_string_pretty(&self.music).unwrap(); - fs::write(format!("./saves/{0}", &self.music.file_name), json).unwrap(); + fs::write(format!("./saves/{0}.pmx", &self.music.file_name), json).unwrap(); + self.all_saves = load_path_saves(); } Message::Load => { - let json = fs::read_to_string(format!("./saves/{0}", &self.music.file_name)); + let json = fs::read_to_string(format!("./saves/{0}.pmx", &self.music.file_name)); match json { Ok(j) => { let decoded: Music = serde_json::from_str(&j).unwrap(); @@ -158,16 +164,22 @@ impl MyApp { Message::ChangeDelta(f) => { self.current_delta = f; self.music.fix_teta(self.current_delta); + // update the red dot on canvas + if self.paused { + self.update(Message::TogglePaused); + self.update(Message::Tick); + self.update(Message::TogglePaused); + } } - _ => {} } } fn view(&self) -> iced::Element { - let txt_nb_rev = format!( - "Number of second for revolution : {}", - self.music.nb_sec_for_rev - ); + 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 entries = self.all_sounds.clone(); @@ -250,8 +262,29 @@ impl MyApp { save_panel.push(button("Load").on_press(Message::Load).into()); } column![ - text("Polymusic").size(32.0), + text(&self.music.file_name).size(32.0), 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![ container( canvas(self.music.current_frame(self.current_delta)) @@ -259,11 +292,6 @@ impl MyApp { .width(Length::FillPortion(1)) ), column![ - text(txt_nb_rev), - row![ - button("Increment").on_press(Message::ButtonPressedIncrement), - button("Decrement").on_press(Message::ButtonPressedDecrement), - ], text("Polygon options"), pick_list(all_options, Some("Choose polygon".to_string()), |s| { Message::AddPolygon(s) @@ -277,7 +305,6 @@ impl MyApp { .height(Length::FillPortion(2)) .spacing(20), column![ - text("TimeLine"), row![ button("Toggle Play").on_press(Message::TogglePaused), text(format!( @@ -285,17 +312,19 @@ impl MyApp { delta_to_string(self.current_delta), delta_to_string(self.music.length) )) - ], - row![ - text("Music Length"), - TextInput::new("MM:SS:CS", &self.str_music_length) - .on_input(|new_value| Message::LengthChange(new_value)), - button("Valid").on_press(Message::SetMusicLength), - ], - slider(0.0..=self.music.length, self.current_delta, move |f| { - Message::ChangeDelta(f) - }) - .step(&self.music.length / 10_000.), + .size(20.0) + ] + .spacing(20), + column![ + slider(0.0..=self.music.length, self.current_delta, move |f| { + Message::ChangeDelta(f) + }) + .step(&self.music.length / 10_000.), + canvas(&self.music) + .height(Length::FillPortion(1)) + .width(Length::FillPortion(1)) + ] + .spacing(0), ] .height(Length::FillPortion(1)) .width(Length::FillPortion(1)), @@ -332,6 +361,14 @@ fn load_path_saves() -> Vec { fs::read_dir("./saves") .unwrap() .filter_map(|res| res.ok()) - .map(|e| e.path().file_name().unwrap().to_str().unwrap().to_string()) + .map(|e| { + e.path() + .file_name() + .unwrap() + .to_str() + .unwrap() + .trim_end_matches(".pmx") + .to_string() + }) .collect() } diff --git a/src/music.rs b/src/music.rs index c000b03..80b2c0a 100644 --- a/src/music.rs +++ b/src/music.rs @@ -1,19 +1,28 @@ use crate::utils::string_to_polygon; use crate::{polygon_draw::*, utils::string_to_color}; +use iced::Length::Fill; use serde::{Deserialize, Serialize}; use kira::{AudioManager, sound::static_sound::StaticSoundData}; +use iced::mouse; +use iced::widget::canvas; +use iced::widget::canvas::Stroke; +use iced::widget::canvas::Style; +use iced::{Color, Rectangle, Renderer, Theme}; +use iced::{Vector, color}; use std::f32::consts::PI; use std::time::Duration; - #[derive(Serialize, Deserialize)] pub struct Music { pub poly_frame: Vec<(f32, PolygonFrame)>, pub nb_sec_for_rev: f32, pub file_name: String, pub length: f32, + #[serde(skip)] teta: f32, + #[serde(skip)] + pub current_delta: f32, } impl Music { @@ -21,7 +30,7 @@ impl Music { if let Some(i) = self .poly_frame .windows(2) - .position(|w| w[0].0 < delta && delta < w[1].0) + .position(|w| w[0].0 <= delta && delta < w[1].0) { &mut self.poly_frame[i].1 } else { @@ -33,7 +42,7 @@ impl Music { if let Some(i) = self .poly_frame .windows(2) - .position(|w| w[0].0 < delta && delta < w[1].0) + .position(|w| w[0].0 <= delta && delta < w[1].0) { &self.poly_frame[i].1 } else { @@ -48,9 +57,10 @@ impl Music { (10.0, PolygonFrame::default()), ], nb_sec_for_rev: 1.0, - file_name: "Polymusic.json".to_string(), + file_name: "Default Name".to_string(), length: 60.0, teta: 0., + current_delta: 0., } } @@ -116,3 +126,60 @@ impl Music { self.find_poly_frame(delta).polygons.remove(i); } } +impl canvas::Program for Music { + // 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 toggle_color = true; + let padding = 8.; + let w = bounds.width - (padding * 2.); + for (delta, _) in &self.poly_frame { + let x = delta / self.length * w + padding; + frame.fill_rectangle( + iced::Point { x: x, y: 0.0 }, + frame.size(), + if toggle_color { + Color::from_rgb(0.3, 0.3, 0.3) + } else { + Color::from_rgb(0.1, 0.1, 0.1) + }, + ); + toggle_color = !toggle_color; + } + frame.stroke_rectangle( + iced::Point { x: 0.0, y: 0.0 }, + frame.size(), + Stroke { + width: 16.0, + ..Stroke::default() + }, + ); + + let x = self.current_delta / self.length * w + padding; + frame.stroke_rectangle( + iced::Point::new(x, 0.), + iced::Size { + width: 0., + height: bounds.height, + }, + Stroke { + width: 4.0, + style: Style::Solid(Color::from_rgb(1.0, 0.0, 0.0)), + ..Stroke::default() + }, + ); + + // Then, we produce the geometry + vec![frame.into_geometry()] + } +}