diff --git a/README.md b/README.md index 94485ac..75699c2 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,14 @@ # shape_maker Eventually, this will be a level editor... But until then, I guess it's just fun? + +## TODO + +[ ] Items tree view showing all shapes with their child nodes and purposes(maybe location and such as well?) +[ ] Delete shapes +[ ] Import images +[ ] Drag camera around +[ ] Zome in/out +[ ] Export +[ ] Import +[ ] Save? (maybe just import and export directly?) diff --git a/src/lib.rs b/src/lib.rs index 75daa35..a192fdb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,8 +7,12 @@ use bevy_egui::egui; mod create; mod helpers; mod modify; +mod rotate; +mod ui; +pub use ui::{action_bar_sys, shape_tree_sys}; pub use modify::modify_sys; pub use create::create_sys; +pub use rotate::rotate_sys; pub use helpers::*; diff --git a/src/main.rs b/src/main.rs index 1910a31..e7796fa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,4 @@ use bevy::ecs::schedule::ShouldRun; -use bevy::math::Vec3Swizzles; use bevy::{prelude::*, window::PresentMode, winit::WinitSettings}; use bevy_egui::{egui, EguiContext, EguiPlugin}; use bevy_prototype_lyon::prelude::*; @@ -38,7 +37,11 @@ fn main() { .add_system(modify_sys.with_run_criteria(|state: Res, mut ec: ResMut| if !ec.ctx_mut().is_pointer_over_area() && state.current_action == Action::Modify { ShouldRun::Yes } else { ShouldRun::No } )) + .add_system(rotate_sys.with_run_criteria(|state: Res, mut ec: ResMut| + if !ec.ctx_mut().is_pointer_over_area() && state.current_action == Action::Rotate { ShouldRun::Yes } else { ShouldRun::No } + )) .add_system(action_bar_sys) + .add_system(shape_tree_sys) ; app.run(); @@ -56,72 +59,4 @@ fn configure_visuals(mut egui_ctx: ResMut) { window_rounding: 0.0.into(), ..Default::default() }); -} - -fn action_bar_sys( - mut egui_ctx: ResMut, - mut state: ResMut, - colors: Res, -) { - egui::Window::new("buttons_float") - .default_pos((50.0, 20.0)) - .title_bar(false) - .resizable(false) - .show(egui_ctx.ctx_mut(), |ui| { - ui.horizontal(|hui| { - let m = hui.button(egui::RichText::new("M").color(state.current_action_color(&colors, Action::Modify))) - .on_hover_text("Modify"); - if m.clicked() { - state.current_action = Action::Modify; - } - let r = hui.button(egui::RichText::new("R").color(state.current_action_color(&colors, Action::Rotate))) - .on_hover_text("Rotate"); - if r.clicked() { - state.current_action = Action::Rotate; - } - let c = hui.button(egui::RichText::new("C").color(state.current_action_color(&colors, Action::Create))) - .on_hover_text("Create"); - if c.clicked() { - state.current_action = Action::Create; - } - let d = hui.button(egui::RichText::new("D").color(state.current_action_color(&colors, Action::Delete))) - .on_hover_text("Delete"); - if d.clicked() { - state.current_action = Action::Delete; - } - hui.label(" | "); - - if hui.button(" I ").on_hover_text("Import Image").clicked() { - println!("Importing Images is still not supported!"); - } - - hui.label(": :"); - }); - - if state.current_action == Action::Create { - ui.horizontal(|hui| { - let tri = hui.button(egui::RichText::new("Tri").color(state.create_shape_color(&colors, CreateShape::Triangle))) - .on_hover_text("Triangle - from 3 edges"); - if tri.clicked() { - state.create_shape = CreateShape::Triangle; - } - let squ = hui.button(egui::RichText::new("Squ").color(state.create_shape_color(&colors, CreateShape::Square))) - .on_hover_text("Square - from 2 opposing edges"); - if squ.clicked() { - state.create_shape = CreateShape::Square; - } - let cir = hui.button(egui::RichText::new("Cir").color(state.create_shape_color(&colors, CreateShape::Circle))) - .on_hover_text("Circle - center point and radius"); - if cir.clicked() { - state.create_shape = CreateShape::Circle; - } - // let cap = hui.button(egui::RichText::new("Cap").color(state.create_shape_color(&colors, CreateShape::Capsule))) - // .on_hover_text("Capsule - from 2 center points and a radius"); - // if cap.clicked() { - // state.create_shape = CreateShape::Capsule; - // } - }); - } - }); -} - +} \ No newline at end of file diff --git a/src/modify.rs b/src/modify.rs index 2c5e403..cfa5126 100644 --- a/src/modify.rs +++ b/src/modify.rs @@ -1,4 +1,4 @@ -use bevy::math::Vec3Swizzles; +use bevy::math::{Vec3Swizzles, Mat2}; use bevy::prelude::*; use crate::*; use bevy_prototype_lyon::prelude::*; @@ -54,18 +54,20 @@ pub fn modify_sys( } else if let Ok(mut t) = transforms.get_mut(pe) { // Update the dot position - let delta = mouse_pos - gtransforms.get(pe).unwrap().translation.xy(); + let gt = gtransforms.get(pe).unwrap(); + let ang = gt.rotation.to_euler(EulerRot::XYZ).2; + let delta = Mat2::from_angle(-ang) * (mouse_pos - gt.translation.xy()); t.translation += delta.extend(0.0); // We need to recalculate the center, and update the points to be the new relevant point from the center - let center_offset = calc_shape_center_offset(&transforms, sd).extend(0.0); + let center_offset = calc_shape_center_offset(&transforms, sd); // Update each edge's offset, and then move the main shape's translation for edge in sd.edges.iter() { if let Ok(mut t) = transforms.get_mut(*edge) { - t.translation -= center_offset; + t.translation -= center_offset.extend(0.0); } } if let Ok(mut t) = transforms.get_mut(sd.main_shape) { - t.translation += center_offset; + t.translation += (Mat2::from_angle(ang) * center_offset).extend(0.0); } // Now we need to update the shape itself diff --git a/src/rotate.rs b/src/rotate.rs new file mode 100644 index 0000000..e6d2125 --- /dev/null +++ b/src/rotate.rs @@ -0,0 +1,44 @@ +use bevy::prelude::*; +use bevy::math::Vec3Swizzles; +use crate::*; + +pub fn rotate_sys( + mut holding: Local>, // main_shape we hold and the angle offset + mouse: Res>, + wnds: Res, + q_cam: Query<(&Camera, &GlobalTransform), With>, + p_size: Res, + mut transforms: Query<&mut Transform>, + gtransforms: Query<&GlobalTransform>, + shapes: Query<&ShapeData>, +) { + if let Some(mouse_pos) = get_mouse_pos(&q_cam, &wnds) { + if mouse.just_pressed(MouseButton::Left) && holding.is_none() { + for sd in shapes.iter().filter(|x| x.shape != CreateShape::Circle /* no reason to rotate circles ;) */) { + for edge in sd.edges.iter() { + if let Ok(gt) = gtransforms.get(*edge) { + if (mouse_pos - gt.translation.xy()).length_squared() < p_size.0.powi(2) { + // We grabbed an entity, we now need an angle offset + let center = gtransforms.get(sd.main_shape).unwrap().translation.xy(); + let angle_offset = (mouse_pos - center).angle_between(Vec2::X); + let angle_offset = transforms.get(sd.main_shape).unwrap().rotation.to_euler(EulerRot::XYZ).2 + angle_offset; + + *holding = Some((sd.main_shape, angle_offset)); + } + } + } + } + } + else if mouse.just_released(MouseButton::Left) { + *holding = None; + } + else if let Some((en, ang_offset)) = *holding { + let center = gtransforms.get(en).unwrap().translation.xy(); + let ang_curr = (mouse_pos - center).angle_between(Vec2::X); + + if let Ok(mut t) = transforms.get_mut(en) { + t.rotation = Quat::from_rotation_z(ang_offset - ang_curr); + } + } + } +} \ No newline at end of file diff --git a/src/ui.rs b/src/ui.rs new file mode 100644 index 0000000..0d5dedd --- /dev/null +++ b/src/ui.rs @@ -0,0 +1,104 @@ +use bevy::prelude::*; +use bevy_egui::{egui, EguiContext}; +use crate::*; + +pub fn action_bar_sys( + mut egui_ctx: ResMut, + mut state: ResMut, + colors: Res, +) { + egui::Window::new("buttons_float") + .default_pos((20.0, 20.0)) + .title_bar(false) + .resizable(false) + .show(egui_ctx.ctx_mut(), |ui| { + ui.horizontal(|hui| { + let m = hui.button(egui::RichText::new("M").color(state.current_action_color(&colors, Action::Modify))) + .on_hover_text("Modify"); + if m.clicked() { + state.current_action = Action::Modify; + } + let r = hui.button(egui::RichText::new("R").color(state.current_action_color(&colors, Action::Rotate))) + .on_hover_text("Rotate"); + if r.clicked() { + state.current_action = Action::Rotate; + } + let c = hui.button(egui::RichText::new("C").color(state.current_action_color(&colors, Action::Create))) + .on_hover_text("Create"); + if c.clicked() { + state.current_action = Action::Create; + } + let d = hui.button(egui::RichText::new("D").color(state.current_action_color(&colors, Action::Delete))) + .on_hover_text("Delete"); + if d.clicked() { + state.current_action = Action::Delete; + } + hui.label(" | "); + + if hui.button(" I ").on_hover_text("Import Image").clicked() { + println!("Importing Images is still not supported!"); + } + + hui.label(": :"); + }); + + if state.current_action == Action::Create { + ui.horizontal(|hui| { + let tri = hui.button(egui::RichText::new("Tri").color(state.create_shape_color(&colors, CreateShape::Triangle))) + .on_hover_text("Triangle - from 3 edges"); + if tri.clicked() { + state.create_shape = CreateShape::Triangle; + } + let squ = hui.button(egui::RichText::new("Squ").color(state.create_shape_color(&colors, CreateShape::Square))) + .on_hover_text("Square - from 2 opposing edges"); + if squ.clicked() { + state.create_shape = CreateShape::Square; + } + let cir = hui.button(egui::RichText::new("Cir").color(state.create_shape_color(&colors, CreateShape::Circle))) + .on_hover_text("Circle - center point and radius"); + if cir.clicked() { + state.create_shape = CreateShape::Circle; + } + // let cap = hui.button(egui::RichText::new("Cap").color(state.create_shape_color(&colors, CreateShape::Capsule))) + // .on_hover_text("Capsule - from 2 center points and a radius"); + // if cap.clicked() { + // state.create_shape = CreateShape::Capsule; + // } + }); + } + }); +} + +pub fn shape_tree_sys( + mut egui_ctx: ResMut, + shapes: Query<&ShapeData>, + mut transforms: Query<&mut Transform>, +) { + if !shapes.is_empty() { + egui::Window::new("Shape Tree") + .default_pos((10.0, 100.0)) + .title_bar(true) + .resizable(false) + .show(egui_ctx.ctx_mut(), |ui| { + for sd in shapes.iter() { + ui.collapsing(format!("{:?}", sd.shape), |ui| { + + if let Ok(mut t) = transforms.get_mut(sd.main_shape) { + ui.horizontal(|hui| { + hui.label("Translation:"); + hui.add(egui::DragValue::new(&mut t.translation.x)); + hui.add(egui::DragValue::new(&mut t.translation.y)); + }); + ui.horizontal(|hui| { + hui.label("Rotation:"); + let mut rot = t.rotation.to_euler(EulerRot::XYZ).2.to_degrees(); + if hui.add(egui::DragValue::new(&mut rot).suffix("°")).changed() { + t.rotation = Quat::from_rotation_z(rot.to_radians()); + } + }); + } + }); + } + }); + } +} \ No newline at end of file