267 lines
9.8 KiB
Rust
267 lines
9.8 KiB
Rust
|
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!(),
|
||
|
}
|
||
|
}
|