vizualizer of different polygon frame

This commit is contained in:
2025-07-08 21:55:05 +02:00
parent 47c41be544
commit 26a96fd933
2 changed files with 141 additions and 37 deletions

View File

@@ -12,12 +12,17 @@ use std::fs;
use iced::{Element, window}; use iced::{Element, window};
use iced::{ use iced::{
Length, Task, Theme, Font,
font::Family,
widget::{ widget::{
Column, TextInput, button, canvas, column, container, pick_list, row, scrollable, slider, TextInput, column, text, text_input,
text, text_input::{Icon, Side},
}, },
}; };
use iced::{
Length, Task, Theme,
widget::{Column, button, canvas, container, pick_list, row, scrollable, slider},
};
use regex::Regex; use regex::Regex;
use std::f32::consts::PI; use std::f32::consts::PI;
@@ -37,7 +42,6 @@ fn main() -> iced::Result {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
enum Message { enum Message {
WindowEvent(window::Event),
ButtonPressedIncrement, ButtonPressedIncrement,
ButtonPressedDecrement, ButtonPressedDecrement,
Tick, Tick,
@@ -89,9 +93,6 @@ impl MyApp {
} }
fn update(&mut self, message: Message) { fn update(&mut self, message: Message) {
match 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::ButtonPressedIncrement => self.music.nb_sec_for_rev += 0.5,
Message::ButtonPressedDecrement => { Message::ButtonPressedDecrement => {
if self.music.nb_sec_for_rev > 0.5 { if self.music.nb_sec_for_rev > 0.5 {
@@ -102,7 +103,11 @@ impl MyApp {
self.music.add_polygon(self.current_delta, s); self.music.add_polygon(self.current_delta, s);
} }
Message::Tick => { Message::Tick => {
if self.current_delta >= self.music.length {
self.paused = true
}
if !self.paused { if !self.paused {
self.music.current_delta = self.current_delta;
let time_btw = Instant::now().duration_since(self.time_last_frame); let time_btw = Instant::now().duration_since(self.time_last_frame);
self.current_delta += time_btw.as_millis() as f32 / 1000.0; self.current_delta += time_btw.as_millis() as f32 / 1000.0;
self.music self.music
@@ -126,10 +131,11 @@ impl MyApp {
} }
Message::Save => { Message::Save => {
let json = serde_json::to_string_pretty(&self.music).unwrap(); 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 => { 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 { match json {
Ok(j) => { Ok(j) => {
let decoded: Music = serde_json::from_str(&j).unwrap(); let decoded: Music = serde_json::from_str(&j).unwrap();
@@ -158,16 +164,22 @@ impl MyApp {
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
if self.paused {
self.update(Message::TogglePaused);
self.update(Message::Tick);
self.update(Message::TogglePaused);
}
} }
_ => {}
} }
} }
fn view(&self) -> iced::Element<Message> { fn view(&self) -> iced::Element<Message> {
let txt_nb_rev = format!( let txt_nb_rev = if self.music.nb_sec_for_rev % 1. != 0.0 {
"Number of second for revolution : {}", format!("{} sec/revolution", self.music.nb_sec_for_rev)
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();
@@ -250,8 +262,29 @@ 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("Polymusic").size(32.0), text(&self.music.file_name).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))
@@ -259,11 +292,6 @@ impl MyApp {
.width(Length::FillPortion(1)) .width(Length::FillPortion(1))
), ),
column![ column![
text(txt_nb_rev),
row![
button("Increment").on_press(Message::ButtonPressedIncrement),
button("Decrement").on_press(Message::ButtonPressedDecrement),
],
text("Polygon options"), text("Polygon options"),
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)
@@ -277,7 +305,6 @@ impl MyApp {
.height(Length::FillPortion(2)) .height(Length::FillPortion(2))
.spacing(20), .spacing(20),
column![ column![
text("TimeLine"),
row![ row![
button("Toggle Play").on_press(Message::TogglePaused), button("Toggle Play").on_press(Message::TogglePaused),
text(format!( text(format!(
@@ -285,17 +312,19 @@ impl MyApp {
delta_to_string(self.current_delta), delta_to_string(self.current_delta),
delta_to_string(self.music.length) delta_to_string(self.music.length)
)) ))
], .size(20.0)
row![ ]
text("Music Length"), .spacing(20),
TextInput::new("MM:SS:CS", &self.str_music_length) column![
.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| { 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)
.height(Length::FillPortion(1))
.width(Length::FillPortion(1))
]
.spacing(0),
] ]
.height(Length::FillPortion(1)) .height(Length::FillPortion(1))
.width(Length::FillPortion(1)), .width(Length::FillPortion(1)),
@@ -332,6 +361,14 @@ fn load_path_saves() -> Vec<String> {
fs::read_dir("./saves") fs::read_dir("./saves")
.unwrap() .unwrap()
.filter_map(|res| res.ok()) .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() .collect()
} }

View File

@@ -1,19 +1,28 @@
use crate::utils::string_to_polygon; use crate::utils::string_to_polygon;
use crate::{polygon_draw::*, utils::string_to_color}; use crate::{polygon_draw::*, utils::string_to_color};
use iced::Length::Fill;
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::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::f32::consts::PI;
use std::time::Duration; use std::time::Duration;
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct Music { pub struct Music {
pub poly_frame: Vec<(f32, PolygonFrame)>, pub poly_frame: Vec<(f32, PolygonFrame)>,
pub nb_sec_for_rev: f32, pub nb_sec_for_rev: f32,
pub file_name: String, pub file_name: String,
pub length: f32, pub length: f32,
#[serde(skip)]
teta: f32, teta: f32,
#[serde(skip)]
pub current_delta: f32,
} }
impl Music { impl Music {
@@ -21,7 +30,7 @@ impl Music {
if let Some(i) = self if let Some(i) = self
.poly_frame .poly_frame
.windows(2) .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 &mut self.poly_frame[i].1
} else { } else {
@@ -33,7 +42,7 @@ impl Music {
if let Some(i) = self if let Some(i) = self
.poly_frame .poly_frame
.windows(2) .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 &self.poly_frame[i].1
} else { } else {
@@ -48,9 +57,10 @@ impl Music {
(10.0, PolygonFrame::default()), (10.0, PolygonFrame::default()),
], ],
nb_sec_for_rev: 1.0, nb_sec_for_rev: 1.0,
file_name: "Polymusic.json".to_string(), file_name: "Default Name".to_string(),
length: 60.0, length: 60.0,
teta: 0., teta: 0.,
current_delta: 0.,
} }
} }
@@ -116,3 +126,60 @@ impl Music {
self.find_poly_frame(delta).polygons.remove(i); self.find_poly_frame(delta).polygons.remove(i);
} }
} }
impl<Message> canvas::Program<Message> for Music {
// 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 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()]
}
}