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, 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 = 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, assets: Res, p_size: Res, ) { 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 = 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 = 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!(), } }