diff --git a/README.md b/README.md index 75699c2..34f2971 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,12 @@ 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?) +- [x] Items tree view showing all shapes with their child nodes and purposes(maybe location and such as well?) +- [ ] Change shape name in tree view +- [ ] Delete shapes in tree view +- [ ] Import images +- [ ] Drag camera around +- [ ] Zome in/out +- [ ] Export +- [ ] Import +- [ ] Save? (maybe just import and export directly?) diff --git a/src/create.rs b/src/create.rs index d544904..9c9dd12 100644 --- a/src/create.rs +++ b/src/create.rs @@ -163,6 +163,7 @@ fn add_center_point( DrawMode::Fill(FillMode::color(Color::RED)), Transform::from_translation(Vec3::new(0.0, 0.0, 1.0)) )) + .insert(Visibility { is_visible: false }) .id(); sd.center = Some(np); @@ -317,6 +318,7 @@ fn create_new_shape(coms: &mut Commands, pos: Vec2, create_shape: CreateShape, p )).id() }, }; + // Get center point(if exists) let center = match create_shape { CreateShape::Circle => Some(fp), @@ -324,5 +326,5 @@ fn create_new_shape(coms: &mut Commands, pos: Vec2, create_shape: CreateShape, p }; let edges = if center.is_none() { Vec::from([fp]) } else { Vec::new() }; - ShapeData { shape: create_shape, main_shape, edges, center } + ShapeData { shape: create_shape, main_shape, edges, center, name: None } } \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index a192fdb..139b2a1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,14 +1,13 @@ #![feature(let_chains)] - use bevy::prelude::*; use bevy_egui::egui; -mod create; -mod helpers; -mod modify; -mod rotate; -mod ui; +pub mod create; +pub mod helpers; +pub mod modify; +pub mod rotate; +pub mod ui; pub use ui::{action_bar_sys, shape_tree_sys}; pub use modify::modify_sys; pub use create::create_sys; @@ -21,6 +20,7 @@ pub struct PointSize(pub f32); #[derive(Component, Debug)] pub struct ShapeData { + pub name: Option, pub shape: CreateShape, pub main_shape: Entity, pub edges: Vec, @@ -36,7 +36,7 @@ pub enum CreateShape { #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum Action { - Create, Modify, Delete, Rotate + Create, Modify, Rotate } pub struct UiState { pub current_action: Action, diff --git a/src/main.rs b/src/main.rs index e7796fa..2ef89e7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ 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::*; @@ -20,7 +21,7 @@ fn main() { }) .insert_resource(ButtonsColors::default()) .insert_resource(UiState::default()) - .insert_resource(PointSize(5.0)) + .insert_resource(PointSize(6.0)) ; app @@ -28,6 +29,7 @@ fn main() { .add_plugin(EguiPlugin) .add_plugin(ShapePlugin) ; + app .add_startup_system(configure_visuals) .add_startup_system(basic_setup_sys) @@ -42,6 +44,8 @@ fn main() { )) .add_system(action_bar_sys) .add_system(shape_tree_sys) + .add_system(show_hide_points) + .add_system(color_points) ; app.run(); @@ -59,4 +63,96 @@ fn configure_visuals(mut egui_ctx: ResMut) { window_rounding: 0.0.into(), ..Default::default() }); +} + + +fn show_hide_points( + state: Res, + shapes: Query<&ShapeData>, + mut dots: Query<&mut Visibility, (With, With)>, +) { + let (center, edge) = match state.current_action { + Action::Create => (false, false), + Action::Modify => (true, true), + Action::Rotate => (true, true), + }; + + for sd in shapes.iter() { + // center + if let Ok(mut v) = dots.get_mut(sd.center.unwrap()) { + if v.is_visible != center { + v.is_visible = center; + } + } + for e in sd.edges.iter() { + if let Ok(mut v) = dots.get_mut(*e) { + if v.is_visible != edge { + v.is_visible = edge; + } + } + } + } +} + +fn color_points( + mut current: Local>, + wnds: Res, + p_size: Res, + q_cam: Query<(&Camera, &GlobalTransform), With>, + shapes: Query<&ShapeData>, + global_transforms: Query<&GlobalTransform, With>, + mut dots: Query<&mut DrawMode, With>, +) { + let normal = DrawMode::Outlined { fill_mode: FillMode::color(Color::RED), outline_mode: StrokeMode::new(Color::rgb(0.9,0.0,0.0), 1.0) }; + let hover = DrawMode::Outlined { fill_mode: FillMode::color(Color::RED), outline_mode: StrokeMode::new(Color::ORANGE, 3.0) }; + + let mouse_pos = get_mouse_pos(&q_cam, &wnds); + if let Some(mpos) = mouse_pos { + let mut hovering = false; + 'shapes: for sd in shapes.iter() { + let e = sd.center.unwrap(); + let p = global_transforms.get(e).unwrap().translation.xy(); + if (mpos - p).length_squared() < p_size.0.powi(2) { + if Some(e) != *current { + if let Some(c) = &*current { + if let Ok(mut md) = dots.get_mut(*c) { + *md = normal; + } + } + *current = Some(e); + if let Ok(mut md) = dots.get_mut(e) { + *md = hover; + } + } + hovering = true; + break; + } + for edge in sd.edges.iter() { + let p = global_transforms.get(*edge).unwrap().translation.xy(); + if (mpos - p).length_squared() < p_size.0.powi(2) { + if Some(*edge) != *current { + if let Some(c) = &*current { + if let Ok(mut md) = dots.get_mut(*c) { + *md = normal; + } + } + *current = Some(*edge); + if let Ok(mut md) = dots.get_mut(*edge) { + *md = hover; + } + } + hovering = true; + break 'shapes; + } + } + } + if !hovering { + if let Some(c) = &*current { + if let Ok(mut md) = dots.get_mut(*c) { + *md = normal; + } + *current = None; + } + } + } } \ No newline at end of file diff --git a/src/modify.rs b/src/modify.rs index cfa5126..8173719 100644 --- a/src/modify.rs +++ b/src/modify.rs @@ -9,7 +9,7 @@ pub fn modify_sys( mouse: Res>, wnds: Res, p_size: Res, - paths: Query<&mut Path>, + mut paths: Query<&mut Path>, mut transforms: Query<&mut Transform>, gtransforms: Query<&GlobalTransform>, q_cam: Query<(&Camera, &GlobalTransform), With>, @@ -71,14 +71,14 @@ pub fn modify_sys( } // Now we need to update the shape itself - update_main_shape(paths, transforms, sd); + update_main_shape(&mut paths, &transforms, sd); } } } } } -fn calc_shape_center_offset(transforms: &Query<&mut Transform>, sd: &ShapeData) -> Vec2 { +pub fn calc_shape_center_offset(transforms: &Query<&mut Transform>, sd: &ShapeData) -> Vec2 { match sd.shape { CreateShape::Triangle => { assert!(sd.edges.len() == 3); @@ -109,7 +109,7 @@ fn calc_shape_center_offset(transforms: &Query<&mut Transform>, sd: &ShapeData) } } -fn update_main_shape(mut paths: Query<&mut Path>, transforms: Query<&mut Transform>, sd: &ShapeData) { +pub fn update_main_shape(paths: &mut Query<&mut Path>, transforms: &Query<&mut Transform>, sd: &ShapeData) { let path = match sd.shape { CreateShape::Triangle => { assert!(sd.edges.len() == 3); diff --git a/src/ui.rs b/src/ui.rs index b31f56d..f95de45 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -1,5 +1,7 @@ +use bevy::math::{Vec3Swizzles, Mat2}; use bevy::prelude::*; use bevy_egui::{egui, EguiContext}; +use bevy_prototype_lyon::prelude::Path; use crate::*; pub fn action_bar_sys( @@ -28,11 +30,6 @@ pub fn action_bar_sys( 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() { @@ -70,9 +67,13 @@ pub fn action_bar_sys( } pub fn shape_tree_sys( + mut name_change: Local>, + mut coms: Commands, mut egui_ctx: ResMut, - shapes: Query<(Entity, &ShapeData)>, + mut shapes: Query<(Entity, &mut ShapeData)>, mut transforms: Query<&mut Transform>, + global_transforms: Query<&GlobalTransform>, + mut paths: Query<&mut Path>, ) { if !shapes.is_empty() { egui::Window::new("Shape Tree") @@ -80,11 +81,9 @@ pub fn shape_tree_sys( .title_bar(true) .resizable(false) .show(egui_ctx.ctx_mut(), |ui| { - for (e, sd) in shapes.iter() { + for (e, mut sd) in shapes.iter_mut () { ui.push_id(e.id(), |ui| { - - ui.collapsing(format!("{:?}", sd.shape), |ui| { - + let ui_func = |ui: &mut egui::Ui| { if let Ok(mut t) = transforms.get_mut(sd.main_shape) { ui.horizontal(|hui| { hui.label("Translation:"); @@ -99,7 +98,61 @@ pub fn shape_tree_sys( } }); } - }); + for (i, edge) in sd.edges.iter().enumerate() { + ui.horizontal(|hui| { + hui.label(format!("Edge {}", i)); + let gt = global_transforms.get(*edge).unwrap(); + let mut gt_x = gt.translation.x; + let mut gt_y = gt.translation.y; + let c1 = hui.add(egui::DragValue::new(&mut gt_x)).changed(); + let c2 = hui.add(egui::DragValue::new(&mut gt_y)).changed(); + + if c1 || c2 { + let ang = gt.rotation.to_euler(EulerRot::XYZ).2; + let delta = Mat2::from_angle(-ang) * (Vec2::new(gt_x, gt_y) - gt.translation.xy()); + if let Ok(mut t) = transforms.get_mut(*edge) { + 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 = modify::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.extend(0.0); + } + } + if let Ok(mut t) = transforms.get_mut(sd.main_shape) { + t.translation += (Mat2::from_angle(ang) * center_offset).extend(0.0); + } + + // Now we need to update the shape itself + modify::update_main_shape(&mut paths, &transforms, &*sd); + } + }); + } + }; + if let Some((sde, name)) = &mut *name_change && *sde == e { + // Changing the current shape's name + let te = egui::TextEdit::singleline(name).desired_width(100.0); + + if ui.add(te).lost_focus() { + let (_, name) = name_change.take().unwrap(); + sd.name = Some(name); + } + } + else { + let default_name = format!("{:?}", sd.shape); + ui.horizontal(|hui| { + let closed = hui.collapsing(sd.name.as_ref().unwrap_or(&default_name), ui_func).fully_closed(); + if closed && hui.button("E").clicked() { + *name_change = Some((e, default_name)); + } + if closed && hui.button("D").clicked() { + coms.entity(sd.main_shape).despawn_recursive(); + coms.entity(e).despawn(); + } + }); + } }); } });