IMPORT AND EXPORT

This commit is contained in:
RustyStriker 2022-08-30 17:54:08 +03:00
parent 43221c02a7
commit ab9e0fd719
7 changed files with 337 additions and 27 deletions

267
src/export.rs Normal file
View file

@ -0,0 +1,267 @@
use std::io::Write;
use bevy::math::Vec3Swizzles;
use bevy::prelude::*;
use bevy_prototype_lyon::prelude::*;
use crate::*;
use serde::{Serialize, Deserialize};
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum ExportData {
Image {
path: String,
scale: Vec2,
},
Square {
size: Vec2,
},
Triangle {
/// Relative to translation!
vertex: [Vec2; 3],
},
Circle {
radius: f32,
}
}
#[derive(Debug, Serialize)]
pub struct ExportItem<'a> {
pub name: &'a str,
pub note: &'a str,
pub translation: Vec3,
pub rotation: f32,
pub color: Color,
pub data: ExportData,
}
#[derive(Debug, Deserialize)]
pub struct ImportItem {
pub name: String,
pub note: String,
pub translation: Vec3,
pub rotation: f32,
pub color: Color,
pub data: ExportData,
}
pub fn toml_save_sys(
mut imp_exp: ResMut<ShouldImportExport>,
shapes: Query<&ShapeData>,
images: Query<(&ImageData, &Transform)>,
transforms: Query<&Transform>,
draw_modes: Query<&DrawMode>,
) {
imp_exp.export = false;
let file = rfd::FileDialog::new()
.set_directory("./")
.set_title("Just pick where you wanna save that file...")
.save_file();
if let Some(path) = file {
let file = std::fs::File::create(path);
if let Ok(mut file) = file {
let items: Vec<ExportItem> = images.iter().map(|(id, tr)| {
ExportItem {
name: &id.name,
note: &id.note,
translation: tr.translation,
rotation: tr.rotation.to_euler(EulerRot::XYZ).2,
data: ExportData::Image { path: id.path.clone(), scale: tr.scale.xy() },
color: Color::WHITE,
}
})
.chain(shapes.iter().map(|sd| {
let trans = transforms.get(sd.main_shape).unwrap();
let color = draw_modes.get(sd.main_shape).map(|dm|
if let DrawMode::Outlined { fill_mode: fm, outline_mode: _ } = dm {
fm.color
} else {
Color::WHITE
}
).unwrap_or(Color::WHITE);
ExportItem {
name: &sd.name,
note: &sd.note,
translation: trans.translation,
rotation: trans.rotation.to_euler(EulerRot::XYZ).2,
data: get_export_data_for_shape(sd, &transforms),
color,
}
}))
.collect();
let json = match serde_json::to_string_pretty(&items) {
Ok(t) => t,
Err(e) => {
println!("json error: {:?}", e);
return;
},
};
file.write(json.as_bytes()).expect("Could not write to save file");
}
}
}
pub fn import_sys(
mut coms: Commands,
mut imp_exp: ResMut<ShouldImportExport>,
assets: Res<AssetServer>,
p_size: Res<PointSize>,
) {
imp_exp.import = false;
let file = rfd::FileDialog::new()
.set_directory("./")
.set_title("Please pick a valid json file to load from <3")
.pick_file();
if let Some(file_path) = file {
let file = std::fs::OpenOptions::new().read(true).open(file_path);
if let Ok(f) = file {
let items: Vec<ImportItem> = match serde_json::from_reader(f) {
Ok(itms) => itms,
Err(_) => {
bevy::log::error!("Invalid json file");
return;
},
};
let outline_mode = StrokeMode::new(Color::rgba(0.0, 0.5, 0.5, 0.6), 3.0);
let dots_dm = DrawMode::Fill(FillMode::color(Color::RED));
let dot_shape = shapes::Circle { radius: p_size.0, center: Vec2::ZERO };
for i in items {
let ImportItem { name, note, translation, rotation, color, data } = i;
let transform = Transform::from_translation(translation)
.with_rotation(Quat::from_rotation_z(rotation));
let center = coms.spawn_bundle(GeometryBuilder::build_as(
&dot_shape,
dots_dm,
Transform::from_xyz(0.0, 0.0, 0.5)
)).id();
let main_shape_dm = DrawMode::Outlined { fill_mode: FillMode::color(color), outline_mode: outline_mode };
match data {
ExportData::Image { path, scale } => {
let image: Handle<Image> = assets.load(&path);
coms.spawn_bundle(SpriteBundle {
texture: image,
..Default::default()
})
.insert(ImageData { name, note, path })
.insert(transform.with_scale(scale.extend(1.0))
);
coms.entity(center).despawn(); // We dont need the center in an image
},
ExportData::Square { size } => {
let e1 = coms.spawn_bundle(GeometryBuilder::build_as(
&dot_shape,
dots_dm,
Transform::from_translation((size * 0.5).extend(0.5))
)).id();
let e2 = coms.spawn_bundle(GeometryBuilder::build_as(
&dot_shape,
dots_dm,
Transform::from_translation((-size * 0.5).extend(0.5))
)).id();
let main_id = coms.spawn_bundle(GeometryBuilder::build_as(
&shapes::Rectangle { extents: size, ..Default::default() },
main_shape_dm,
transform
)).id();
coms.entity(main_id).insert(ShapeData {
name,
shape: CreateShape::Square,
main_shape: main_id,
edges: Vec::from([e1,e2]),
center: Some(center),
note,
})
.add_child(center)
.add_child(e1)
.add_child(e2);
},
ExportData::Triangle { vertex } => {
let main_shape = shapes::Polygon { points: vertex.clone().to_vec(), closed: true };
let vertex = vertex.map(|v| {
coms.spawn_bundle(GeometryBuilder::build_as(
&dot_shape,
dots_dm,
Transform::from_translation(v.extend(0.5))
)).id()
});
let main_id = coms.spawn_bundle(GeometryBuilder::build_as(
&main_shape,
main_shape_dm,
transform
)).id();
coms.entity(main_id).insert(ShapeData {
name,
shape: CreateShape::Triangle,
main_shape: main_id,
edges: vertex.clone().to_vec(),
center: Some(center),
note,
})
.add_child(vertex[0])
.add_child(vertex[1])
.add_child(vertex[2])
.add_child(center);
},
ExportData::Circle { radius } => {
let edge = coms.spawn_bundle(GeometryBuilder::build_as(
&dot_shape,
dots_dm,
Transform::from_xyz(radius, 0.0, 0.5)
)).id();
let main_id = coms.spawn_bundle(GeometryBuilder::build_as(
&shapes::Circle { radius, center: Vec2::ZERO },
main_shape_dm,
transform
)).id();
coms.entity(main_id).insert(ShapeData {
name,
shape: CreateShape::Circle,
main_shape: main_id,
edges: [edge].to_vec(),
center: Some(center),
note,
})
.add_child(center)
.add_child(edge);
},
}
}
}
}
}
fn get_export_data_for_shape(sd: &ShapeData, transforms: &Query<&Transform>,) -> ExportData {
match sd.shape {
CreateShape::Triangle => {
assert_eq!(sd.edges.len(), 3);
let mut edges = sd.edges.iter().take(3).map(|e| transforms.get(*e).unwrap().translation.xy());
let edges = [edges.next().unwrap(), edges.next().unwrap(), edges.next().unwrap()];
ExportData::Triangle { vertex: edges }
},
CreateShape::Square => {
assert_eq!(sd.edges.len(), 2);
let e1 = transforms.get(sd.edges[0]).unwrap().translation.xy();
let e2 = transforms.get(sd.edges[1]).unwrap().translation.xy();
let size = (e1 - e2).abs();
ExportData::Square { size }
},
CreateShape::Circle => {
assert_eq!(sd.edges.len(), 1);
let radius = transforms.get(sd.edges[0]).unwrap().translation.xy().length();
ExportData::Circle { radius }
},
CreateShape::Capsule => unimplemented!(),
}
}

View file

@ -6,6 +6,7 @@ pub mod helpers;
pub mod modify;
pub mod ui;
pub mod infinite_grid;
pub mod export;
pub use modify::modify_sys;
pub use create::create_sys;
pub use helpers::*;
@ -40,10 +41,12 @@ impl Default for SnapGrid {
#[derive(Clone, Debug)]
pub struct PointSize(pub f32);
#[derive(Component, Debug, Deref, DerefMut)]
pub struct ImageName(pub String);
#[derive(Component, Debug, Deref, DerefMut)]
pub struct ImageSize(pub Vec2);
#[derive(Component, Debug)]
pub struct ImageData {
pub name: String,
pub note: String,
pub path: String,
}
#[derive(Component, Debug)]
pub struct ShapeData {
@ -96,6 +99,11 @@ impl UiState {
}
}
}
#[derive(Default, Clone, Debug)]
pub struct ShouldImportExport {
pub import: bool,
pub export: bool,
}
#[derive(Debug)]
pub struct ButtonsColors {

View file

@ -27,6 +27,7 @@ fn main() {
.insert_resource(PointSize(6.0))
.insert_resource(SnapGrid::default())
.insert_resource(ui::SelectedItem::default())
.insert_resource(ShouldImportExport::default())
;
app
@ -64,6 +65,12 @@ fn main() {
.add_system(delete_selected_item_on_del_sys.with_run_criteria(|mut ec: ResMut<EguiContext>|
if ec.ctx_mut().wants_keyboard_input() { ShouldRun::No } else { ShouldRun::Yes }
))
.add_system(export::toml_save_sys.with_run_criteria(|ie: Res<ShouldImportExport>| {
if ie.export { ShouldRun::Yes } else { ShouldRun::No }
}))
.add_system(export::import_sys.with_run_criteria(|ie: Res<ShouldImportExport>| {
if ie.import { ShouldRun::Yes} else { ShouldRun::No }
}))
;
app.run();

View file

@ -40,12 +40,13 @@ pub fn action_bar_sys(
.pick_file();
if let Some(file) = file {
let name = String::from(file.file_name().unwrap().to_str().unwrap_or("Image"));
let path = file.to_str().unwrap().to_string();
let image: Handle<Image> = assets.load(file);
coms.spawn_bundle(SpriteBundle {
texture: image,
..Default::default()
})
.insert(ImageName(name));
.insert(ImageData { name, note: String::new(), path });
}
}
@ -118,17 +119,29 @@ pub fn grid_window_sys(
/// TODO: Allow drag and drop to re-order + reparent
pub fn items_tree_sys(
mut selected: ResMut<SelectedItem>,
mut imp_exp: ResMut<ShouldImportExport>,
mut egui_ctx: ResMut<EguiContext>,
shapes:Query<(Entity, &ShapeData)>,
images: Query<(Entity, &ImageName), With<Sprite>>,
images: Query<(Entity, &ImageData), With<Sprite>>,
mut draw_modes: Query<&mut DrawMode, With<ShapeData>>,
mut visible: Query<&mut Visibility, Or<(With<Path>, With<Sprite>)>>,
) {
egui::Window::new("Items")
.default_pos((10.0,100.0))
.fixed_size((150.0, f32::INFINITY))
.title_bar(true)
.resizable(false)
.show(egui_ctx.ctx_mut(), |ui| {
ui.horizontal(|hui| {
if hui.button("Import").clicked() {
imp_exp.import = true;
}
if hui.button("Export").clicked() {
imp_exp.export = true;
}
});
ui.separator();
for (e, sd) in shapes.iter() {
let entity_selected = if let Some(se) = **selected && se == e { true } else { false };
ui.horizontal(|hui| {
@ -162,7 +175,7 @@ pub fn items_tree_sys(
ui.horizontal(|hui| {
let color = if entity_selected { egui::Color32::GOLD } else { egui::Color32::WHITE };
let label = egui::Label::new(egui::RichText::new(&**n).color(color)).sense(egui::Sense::click());
let label = egui::Label::new(egui::RichText::new(&n.name).color(color)).sense(egui::Sense::click());
if hui.add(label).clicked() {
if entity_selected {
**selected = None;
@ -180,7 +193,6 @@ pub fn items_tree_sys(
}
});
}
});
}
@ -194,7 +206,7 @@ pub fn inspector_sys(
mut paths: Query<&mut Path>,
mut draw_modes: Query<&mut DrawMode, With<Path>>,
mut visible: Query<&mut Visibility, Or<(With<Path>, With<Sprite>)>>,
mut images: Query<&mut ImageName, With<Sprite>>,
mut images: Query<&mut ImageData, With<Sprite>>,
) {
if let Some(e) = **selected {
let mut open = true;
@ -294,7 +306,7 @@ pub fn inspector_sys(
else if let Ok(mut name) = images.get_mut(e) {
ui.horizontal(|hui| {
hui.label("Name:");
hui.text_edit_singleline(&mut **name);
hui.text_edit_singleline(&mut name.name);
});
ui.separator();