historic , undo and redo
This commit is contained in:
@@ -3,11 +3,11 @@ use crate::message::Message;
|
|||||||
use iced::alignment::{Horizontal, Vertical};
|
use iced::alignment::{Horizontal, Vertical};
|
||||||
|
|
||||||
use iced::widget::{TextInput, column, text};
|
use iced::widget::{TextInput, column, text};
|
||||||
|
use iced::{Element, Theme};
|
||||||
use iced::{
|
use iced::{
|
||||||
Length, Padding,
|
Length, Padding,
|
||||||
widget::{Column, Container, button, canvas, container, pick_list, row, scrollable, slider},
|
widget::{Column, Container, button, canvas, container, pick_list, row, scrollable, slider},
|
||||||
};
|
};
|
||||||
use iced::{Element, Theme};
|
|
||||||
use iced_aw::menu::{self, Item};
|
use iced_aw::menu::{self, Item};
|
||||||
use iced_aw::menu_bar;
|
use iced_aw::menu_bar;
|
||||||
use iced_aw::widget::color_picker;
|
use iced_aw::widget::color_picker;
|
||||||
@@ -115,7 +115,9 @@ pub fn music_view(app: &MyApp) -> iced::Element<Message> {
|
|||||||
menu_tpl_1(vec![
|
menu_tpl_1(vec![
|
||||||
Item::new(" SPACE"),
|
Item::new(" SPACE"),
|
||||||
Item::new(" CTRL+S"),
|
Item::new(" CTRL+S"),
|
||||||
Item::new("Start ")
|
Item::new(" ARROW UP"),
|
||||||
|
Item::new(" CTRL+Z"),
|
||||||
|
Item::new(" CTRL+Y"),
|
||||||
])
|
])
|
||||||
))
|
))
|
||||||
.draw_path(menu::DrawPath::Backdrop)
|
.draw_path(menu::DrawPath::Backdrop)
|
||||||
|
|||||||
53
src/history.rs
Normal file
53
src/history.rs
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
use crate::{message::Message, music::Music};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
struct HistoricItem {
|
||||||
|
old: Message,
|
||||||
|
new: Message,
|
||||||
|
delta: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Historic {
|
||||||
|
past: Vec<HistoricItem>,
|
||||||
|
future: Vec<HistoricItem>,
|
||||||
|
pub ignore_add: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Historic {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Historic {
|
||||||
|
past: vec![],
|
||||||
|
future: vec![],
|
||||||
|
ignore_add: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(&mut self, old: Message, new: Message, delta: f32) {
|
||||||
|
if !self.ignore_add {
|
||||||
|
self.future = vec![];
|
||||||
|
self.past.push(HistoricItem {
|
||||||
|
old: old,
|
||||||
|
new: new,
|
||||||
|
delta: delta,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn undo(&mut self) -> Option<(Message, f32)> {
|
||||||
|
if let Some(item) = self.past.pop() {
|
||||||
|
self.future.push(item.clone());
|
||||||
|
Some((item.old.clone(), item.delta))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn redo(&mut self) -> Option<(Message, f32)> {
|
||||||
|
if let Some(item) = self.future.pop() {
|
||||||
|
self.past.push(item.clone());
|
||||||
|
Some((item.new.clone(), item.delta))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
113
src/main.rs
113
src/main.rs
@@ -5,6 +5,9 @@ use polygon_draw::Polygon;
|
|||||||
mod music;
|
mod music;
|
||||||
use music::Music;
|
use music::Music;
|
||||||
|
|
||||||
|
mod history;
|
||||||
|
use history::Historic;
|
||||||
|
|
||||||
mod message;
|
mod message;
|
||||||
use message::Message;
|
use message::Message;
|
||||||
|
|
||||||
@@ -76,6 +79,7 @@ struct MyApp {
|
|||||||
mode_file_load: bool,
|
mode_file_load: bool,
|
||||||
file_name_to_creat: String,
|
file_name_to_creat: String,
|
||||||
show_warning_message_creat: bool,
|
show_warning_message_creat: bool,
|
||||||
|
historic: Historic,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MyApp {
|
impl MyApp {
|
||||||
@@ -83,8 +87,6 @@ impl MyApp {
|
|||||||
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(),
|
||||||
@@ -101,6 +103,7 @@ impl MyApp {
|
|||||||
mode_file_load: true,
|
mode_file_load: true,
|
||||||
file_name_to_creat: "Default File Name".to_string(),
|
file_name_to_creat: "Default File Name".to_string(),
|
||||||
show_warning_message_creat: false,
|
show_warning_message_creat: false,
|
||||||
|
historic: Historic::new(),
|
||||||
},
|
},
|
||||||
Task::none(),
|
Task::none(),
|
||||||
)
|
)
|
||||||
@@ -108,8 +111,13 @@ impl MyApp {
|
|||||||
fn update(&mut self, message: Message) {
|
fn update(&mut self, message: Message) {
|
||||||
match message {
|
match message {
|
||||||
Message::AddPolygon(s) => {
|
Message::AddPolygon(s) => {
|
||||||
self.music.add_polygon(self.current_delta, s);
|
self.music.add_polygon(self.current_delta, s.clone());
|
||||||
self.already_save = false;
|
self.already_save = false;
|
||||||
|
self.historic.add(
|
||||||
|
Message::Remove(usize::MAX),
|
||||||
|
Message::AddPolygon(s),
|
||||||
|
self.current_delta,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Message::Tick => {
|
Message::Tick => {
|
||||||
if self.current_delta >= self.music.length {
|
if self.current_delta >= self.music.length {
|
||||||
@@ -126,19 +134,36 @@ impl MyApp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Message::Remove(i) => {
|
Message::Remove(i) => {
|
||||||
self.music.remove_polygon(self.current_delta, i);
|
let name = self.music.remove_polygon(self.current_delta, i);
|
||||||
self.already_save = false;
|
self.already_save = false;
|
||||||
|
self.historic.add(
|
||||||
|
Message::AddPolygon(name),
|
||||||
|
Message::Remove(i),
|
||||||
|
self.current_delta,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Message::ChangeTeta(i, teta) => {
|
Message::ChangeTeta(i, teta) => {
|
||||||
self.music.set_teta(self.current_delta, i, teta);
|
let old_teta = self.music.set_teta(self.current_delta, i, teta);
|
||||||
self.already_save = false;
|
self.already_save = false;
|
||||||
|
self.historic.add(
|
||||||
|
Message::ChangeTeta(i, old_teta),
|
||||||
|
Message::ChangeTeta(i, teta),
|
||||||
|
self.current_delta,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
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");
|
||||||
self.music.set_sound(self.current_delta, i, sound, s);
|
let old_sound = self
|
||||||
|
.music
|
||||||
|
.set_sound(self.current_delta, i, sound, s.clone());
|
||||||
self.already_save = false;
|
self.already_save = false;
|
||||||
|
self.historic.add(
|
||||||
|
Message::ChangeSound(i, old_sound),
|
||||||
|
Message::ChangeSound(i, s),
|
||||||
|
self.current_delta,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Message::Save => {
|
Message::Save => {
|
||||||
let json = serde_json::to_string_pretty(&self.music).unwrap();
|
let json = serde_json::to_string_pretty(&self.music).unwrap();
|
||||||
@@ -176,6 +201,11 @@ impl MyApp {
|
|||||||
if is_delta_format_valid(&s) {
|
if is_delta_format_valid(&s) {
|
||||||
let sec = str_to_sec(&s);
|
let sec = str_to_sec(&s);
|
||||||
if sec > 0. {
|
if sec > 0. {
|
||||||
|
self.historic.add(
|
||||||
|
Message::LengthChange(delta_to_string(self.music.length)),
|
||||||
|
Message::LengthChange(s.clone()),
|
||||||
|
self.current_delta,
|
||||||
|
);
|
||||||
self.music.length = sec;
|
self.music.length = sec;
|
||||||
self.already_save = false;
|
self.already_save = false;
|
||||||
}
|
}
|
||||||
@@ -202,13 +232,24 @@ impl MyApp {
|
|||||||
}
|
}
|
||||||
self.str_time = s;
|
self.str_time = s;
|
||||||
}
|
}
|
||||||
|
Message::ReAddPoint => {
|
||||||
|
self.already_save = false;
|
||||||
|
self.music.add_point_old();
|
||||||
|
}
|
||||||
Message::AddPoint => {
|
Message::AddPoint => {
|
||||||
self.already_save = false;
|
self.already_save = false;
|
||||||
self.music.add_point(self.current_delta);
|
self.music.add_point(self.current_delta);
|
||||||
|
self.historic
|
||||||
|
.add(Message::RemovePoint, Message::AddPoint, self.current_delta);
|
||||||
}
|
}
|
||||||
Message::RemovePoint => {
|
Message::RemovePoint => {
|
||||||
self.already_save = false;
|
self.already_save = false;
|
||||||
self.music.remove_point(self.current_delta);
|
self.music.remove_point(self.current_delta);
|
||||||
|
self.historic.add(
|
||||||
|
Message::ReAddPoint,
|
||||||
|
Message::RemovePoint,
|
||||||
|
self.current_delta,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Message::ClickedOnTimeLine(f) => {
|
Message::ClickedOnTimeLine(f) => {
|
||||||
self.update(Message::ChangeDelta(f));
|
self.update(Message::ChangeDelta(f));
|
||||||
@@ -218,12 +259,22 @@ impl MyApp {
|
|||||||
self.music.slide_to_left(self.current_delta);
|
self.music.slide_to_left(self.current_delta);
|
||||||
self.music.fix_teta(self.current_delta);
|
self.music.fix_teta(self.current_delta);
|
||||||
self.update_canvas_if_paused();
|
self.update_canvas_if_paused();
|
||||||
|
self.historic.add(
|
||||||
|
Message::SlidePointRight,
|
||||||
|
Message::SlidePointLeft,
|
||||||
|
self.current_delta,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Message::SlidePointRight => {
|
Message::SlidePointRight => {
|
||||||
self.already_save = false;
|
self.already_save = false;
|
||||||
self.music.slide_to_right(self.current_delta);
|
self.music.slide_to_right(self.current_delta);
|
||||||
self.music.fix_teta(self.current_delta);
|
self.music.fix_teta(self.current_delta);
|
||||||
self.update_canvas_if_paused();
|
self.update_canvas_if_paused();
|
||||||
|
self.historic.add(
|
||||||
|
Message::SlidePointLeft,
|
||||||
|
Message::SlidePointRight,
|
||||||
|
self.current_delta,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Message::ChangeDegree(i, s) => {
|
Message::ChangeDegree(i, s) => {
|
||||||
let mut mut_s = s;
|
let mut mut_s = s;
|
||||||
@@ -247,6 +298,14 @@ impl MyApp {
|
|||||||
Ok(val) => {
|
Ok(val) => {
|
||||||
let val = (val * 10.).floor() / 10.;
|
let val = (val * 10.).floor() / 10.;
|
||||||
if val >= 1. && val < 1000. {
|
if val >= 1. && val < 1000. {
|
||||||
|
self.historic.add(
|
||||||
|
Message::ChangeNbPerSec(format!(
|
||||||
|
"{:.1} sec/rev",
|
||||||
|
self.music.nb_sec_for_rev
|
||||||
|
)),
|
||||||
|
Message::ChangeNbPerSec(s.clone()),
|
||||||
|
self.current_delta,
|
||||||
|
);
|
||||||
self.music.nb_sec_for_rev = val;
|
self.music.nb_sec_for_rev = val;
|
||||||
self.already_save = false;
|
self.already_save = false;
|
||||||
}
|
}
|
||||||
@@ -267,14 +326,19 @@ impl MyApp {
|
|||||||
self.music.set_color_picker(self.current_delta, i, true);
|
self.music.set_color_picker(self.current_delta, i, true);
|
||||||
}
|
}
|
||||||
Message::ChooseColor(i, color) => {
|
Message::ChooseColor(i, color) => {
|
||||||
self.music.set_color(self.current_delta, i, color);
|
let old_color = self.music.set_color(self.current_delta, i, color);
|
||||||
self.music.set_color_picker(self.current_delta, i, false);
|
self.music.set_color_picker(self.current_delta, i, false);
|
||||||
self.can_unpaused = true;
|
self.can_unpaused = true;
|
||||||
self.already_save = false;
|
self.already_save = false;
|
||||||
|
self.historic.add(
|
||||||
|
Message::ChooseColor(i, old_color),
|
||||||
|
Message::ChooseColor(i, color),
|
||||||
|
self.current_delta,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Message::None => {}
|
|
||||||
Message::GoToLoadView => {
|
Message::GoToLoadView => {
|
||||||
if self.already_save {
|
if self.already_save {
|
||||||
|
self.historic = Historic::new();
|
||||||
self.paused = true;
|
self.paused = true;
|
||||||
self.file_name_to_creat = "Default File Name".to_string();
|
self.file_name_to_creat = "Default File Name".to_string();
|
||||||
self.show_warning_message_creat = false;
|
self.show_warning_message_creat = false;
|
||||||
@@ -284,6 +348,23 @@ impl MyApp {
|
|||||||
Message::ForceToQuit => {
|
Message::ForceToQuit => {
|
||||||
self.already_save = true;
|
self.already_save = true;
|
||||||
}
|
}
|
||||||
|
Message::Undo => {
|
||||||
|
if let Some(item) = self.historic.undo() {
|
||||||
|
self.historic.ignore_add = true;
|
||||||
|
self.update(Message::ChangeDelta(item.1));
|
||||||
|
self.update(item.0);
|
||||||
|
self.historic.ignore_add = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Message::Redo => {
|
||||||
|
if let Some(item) = self.historic.redo() {
|
||||||
|
self.historic.ignore_add = true;
|
||||||
|
self.update(Message::ChangeDelta(item.1));
|
||||||
|
self.update(item.0);
|
||||||
|
self.historic.ignore_add = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Message::None => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -297,6 +378,22 @@ impl MyApp {
|
|||||||
|
|
||||||
fn subscription(&self) -> iced::Subscription<Message> {
|
fn subscription(&self) -> iced::Subscription<Message> {
|
||||||
let subscr_key = event::listen_with(|event, status, _| match (event, status) {
|
let subscr_key = event::listen_with(|event, status, _| match (event, status) {
|
||||||
|
(
|
||||||
|
Event::Keyboard(iced::keyboard::Event::KeyPressed {
|
||||||
|
key: Key::Character(c),
|
||||||
|
modifiers: Modifiers::CTRL,
|
||||||
|
..
|
||||||
|
}),
|
||||||
|
Status::Ignored,
|
||||||
|
) if c.as_ref() == "z" => Some(Message::Undo),
|
||||||
|
(
|
||||||
|
Event::Keyboard(iced::keyboard::Event::KeyPressed {
|
||||||
|
key: Key::Character(c),
|
||||||
|
modifiers: Modifiers::CTRL,
|
||||||
|
..
|
||||||
|
}),
|
||||||
|
Status::Ignored,
|
||||||
|
) if c.as_ref() == "y" => Some(Message::Redo),
|
||||||
(
|
(
|
||||||
Event::Keyboard(iced::keyboard::Event::KeyPressed {
|
Event::Keyboard(iced::keyboard::Event::KeyPressed {
|
||||||
key: Key::Character(c),
|
key: Key::Character(c),
|
||||||
|
|||||||
@@ -7,27 +7,32 @@ pub enum Message {
|
|||||||
SetFileNameCreat(String),
|
SetFileNameCreat(String),
|
||||||
GoToLoadView,
|
GoToLoadView,
|
||||||
ForceToQuit,
|
ForceToQuit,
|
||||||
ChangeNbPerSec(String),
|
|
||||||
Tick,
|
Tick,
|
||||||
AddPolygon(String),
|
|
||||||
ChangeTeta(usize, f32),
|
|
||||||
Remove(usize),
|
|
||||||
ChangeSound(usize, String),
|
|
||||||
Save,
|
Save,
|
||||||
Load,
|
Load,
|
||||||
FileNameChanged(String),
|
FileNameChanged(String),
|
||||||
TogglePaused,
|
TogglePaused,
|
||||||
LengthChange(String),
|
|
||||||
ChangeDelta(f32),
|
ChangeDelta(f32),
|
||||||
AddPoint,
|
|
||||||
RemovePoint,
|
|
||||||
ClickedOnTimeLine(f32),
|
ClickedOnTimeLine(f32),
|
||||||
ChangeDeltaString(String),
|
ChangeDeltaString(String),
|
||||||
|
|
||||||
|
ChangeNbPerSec(String),
|
||||||
|
AddPolygon(String),
|
||||||
|
ChangeTeta(usize, f32),
|
||||||
|
Remove(usize),
|
||||||
|
ChangeSound(usize, String),
|
||||||
|
LengthChange(String),
|
||||||
|
AddPoint,
|
||||||
|
RemovePoint,
|
||||||
SlidePointLeft,
|
SlidePointLeft,
|
||||||
SlidePointRight,
|
SlidePointRight,
|
||||||
ChangeDegree(usize, String),
|
ChangeDegree(usize, String),
|
||||||
|
ReAddPoint,
|
||||||
|
|
||||||
ChooseColor(usize, Color),
|
ChooseColor(usize, Color),
|
||||||
CancelColor(usize),
|
CancelColor(usize),
|
||||||
SubmitColor(usize),
|
SubmitColor(usize),
|
||||||
|
|
||||||
|
Undo,
|
||||||
|
Redo,
|
||||||
}
|
}
|
||||||
|
|||||||
36
src/music.rs
36
src/music.rs
@@ -31,6 +31,8 @@ pub struct Music {
|
|||||||
teta: f32,
|
teta: f32,
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub current_delta: f32,
|
pub current_delta: f32,
|
||||||
|
#[serde(skip)]
|
||||||
|
point_removed: Vec<(f32, PolygonFrame)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Music {
|
impl Music {
|
||||||
@@ -77,6 +79,7 @@ impl Music {
|
|||||||
length: 60.0,
|
length: 60.0,
|
||||||
teta: 0.,
|
teta: 0.,
|
||||||
current_delta: 0.,
|
current_delta: 0.,
|
||||||
|
point_removed: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,19 +119,25 @@ impl Music {
|
|||||||
index: usize,
|
index: usize,
|
||||||
sound: StaticSoundData,
|
sound: StaticSoundData,
|
||||||
sound_name: String,
|
sound_name: String,
|
||||||
) {
|
) -> String {
|
||||||
let current_frame = self.find_poly_frame(delta);
|
let current_frame = self.find_poly_frame(delta);
|
||||||
current_frame.polygons[index].sound = sound;
|
current_frame.polygons[index].sound = sound;
|
||||||
|
let out = current_frame.polygons[index].sound_name.clone();
|
||||||
current_frame.polygons[index].sound_name = sound_name;
|
current_frame.polygons[index].sound_name = sound_name;
|
||||||
|
out
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_teta(&mut self, delta: f32, index: usize, teta: f32) {
|
pub fn set_teta(&mut self, delta: f32, index: usize, teta: f32) -> f32 {
|
||||||
|
let out = self.find_poly_frame(delta).polygons[index].global_teta;
|
||||||
self.find_poly_frame(delta).polygons[index].global_teta = teta;
|
self.find_poly_frame(delta).polygons[index].global_teta = teta;
|
||||||
|
out
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_color(&mut self, delta: f32, index: usize, color: Color) {
|
pub fn set_color(&mut self, delta: f32, index: usize, color: Color) -> Color {
|
||||||
let current_frame = self.find_poly_frame(delta);
|
let current_frame = self.find_poly_frame(delta);
|
||||||
|
let out = current_frame.polygons[index].color.clone();
|
||||||
current_frame.polygons[index].color = color;
|
current_frame.polygons[index].color = color;
|
||||||
|
out
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_color_picker(&mut self, delta: f32, i: usize, b: bool) {
|
pub fn set_color_picker(&mut self, delta: f32, i: usize, b: bool) {
|
||||||
@@ -146,9 +155,19 @@ 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 add_point_old(&mut self) {
|
||||||
|
if let Some(pair) = self.point_removed.pop() {
|
||||||
|
let pos = self
|
||||||
|
.poly_frame
|
||||||
|
.binary_search_by(|(d, _)| d.partial_cmp(&pair.0).unwrap())
|
||||||
|
.unwrap_or_else(|e| e);
|
||||||
|
self.poly_frame.insert(pos, pair);
|
||||||
|
}
|
||||||
|
}
|
||||||
pub fn remove_point(&mut self, delta: f32) {
|
pub fn remove_point(&mut self, delta: f32) {
|
||||||
let i = self.find_index_frame(delta);
|
let i = self.find_index_frame(delta);
|
||||||
if i != 0 {
|
if i != 0 {
|
||||||
|
self.point_removed.push(self.poly_frame[i].clone());
|
||||||
self.poly_frame.remove(i);
|
self.poly_frame.remove(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -159,8 +178,15 @@ impl Music {
|
|||||||
current_frame.polygons.push(poly);
|
current_frame.polygons.push(poly);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_polygon(&mut self, delta: f32, i: usize) {
|
pub fn remove_polygon(&mut self, delta: f32, i: usize) -> String {
|
||||||
self.find_poly_frame(delta).polygons.remove(i);
|
let pf = self.find_poly_frame(delta);
|
||||||
|
let mut i = i;
|
||||||
|
if i == usize::MAX {
|
||||||
|
i = pf.polygons.len() - 1
|
||||||
|
}
|
||||||
|
let out = pf.polygons[i].name.clone();
|
||||||
|
pf.polygons.remove(i);
|
||||||
|
out
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn slide_to_left(&mut self, delta: f32) {
|
pub fn slide_to_left(&mut self, delta: f32) {
|
||||||
|
|||||||
Reference in New Issue
Block a user