use bevy::math::{Vec3Swizzles, Mat2}; use bevy::prelude::*; use crate::*; use bevy_prototype_lyon::prelude::*; #[derive(Default, PartialEq)] pub enum Holding { /// (ShapeData, Path) Shape(Entity, Entity), /// (Sprite, Offset) Image(Entity, Vec2), #[default] None, } pub fn modify_sys( // Which entity the user currently drags, and which specific part of it(ShapeData entity, path entity) mut holding: Local, mut held_from: Local, mut undo: ResMut, mouse: Res>, wnds: Res, scale: Query<&OrthographicProjection, With>, p_size: Res, assets: Res>, mut paths: Query<&mut Path>, mut transforms: Query<&mut Transform>, gtransforms: Query<&GlobalTransform>, q_cam: Query<(&Camera, &GlobalTransform), With>, shapes: Query<(Entity, &ShapeData)>, images: Query<(Entity, &Handle)>, snap: Res, ) { let scale = scale.get_single().map(|s| s.scale).unwrap_or(1.0); let mouse_pos = get_mouse_pos(&q_cam, &wnds); if let Some(mouse_pos) = mouse_pos { if mouse.just_pressed(MouseButton::Left) && *holding == Holding::None { // Grab attempt - search if we are holding a shape or something for (e, sd) in shapes.iter() { if let Ok(t) = gtransforms.get(sd.center.unwrap()) { let t = t.translation().xy(); if (mouse_pos - t).length_squared() < (p_size.0 * scale).powi(2) { *held_from = t; *holding = Holding::Shape(e, sd.center.unwrap()); break; } } for edge in sd.edges.iter() { if let Ok(t) = gtransforms.get(*edge) { let t = t.translation().xy(); if (mouse_pos - t).length_squared() < (p_size.0 * scale).powi(2) { *held_from = t; *holding = Holding::Shape(e, *edge); break; } } } } // Only proceed if holding is still None if *holding == Holding::None { for (e, h) in images.iter() { if let Some(size) = assets.get(h).map(|x| x.size()) { let size = size * 0.5; let t = gtransforms.get(e).unwrap(); let scale = t.to_scale_rotation_translation().0; // Disregard rotations for now plz let diff = (mouse_pos - t.translation().xy()).abs(); if diff.x < size.x * scale.x && diff.y < size.y * scale.y { *held_from = t.translation().xy(); *holding = Holding::Image(e, mouse_pos - t.translation().xy()); break; } } } } } else if mouse.just_released(MouseButton::Left) && *holding != Holding::None { match *holding { Holding::Shape(shape_data, dot) => { if let Ok(sd) = shapes.get_component::(shape_data) { if sd.center == Some(dot) { undo.push(UndoItem::MoveItem { shape: shape_data, from: *held_from, to: snap.snap_to_grid(mouse_pos) }); } else { undo.push(UndoItem::MoveDot { shape_data, dot: sd.edges.iter().position(|e| *e == dot).unwrap_or(0), from: *held_from, to: snap.snap_to_grid(mouse_pos), }); } } }, Holding::Image(e, off) => { // ye i know it says MoVeShaPe but really it doesnt matter that much here! undo.push(UndoItem::MoveItem { shape: e, from: *held_from, to: snap.snap_to_grid(mouse_pos - off) }); }, Holding::None => {}, } *holding = Holding::None; // We just released our sad little dot/shape } else if let Holding::Shape(se, pe) = *holding { if let Ok(sd) = shapes.get_component::(se) { // Middle of a drag :D if let Some(ce) = sd.center && ce == pe { if let Ok(mut t) = transforms.get_mut(sd.main_shape) { t.translation = snap.snap_to_grid(mouse_pos).extend(t.translation.z); } } else if let Ok(mut t) = transforms.get_mut(pe) { // Update the dot position let gt = gtransforms.get(pe).unwrap(); let rot = gt.to_scale_rotation_translation().1; let ang = rot.to_euler(EulerRot::XYZ).2; let delta = Mat2::from_angle(-ang) * (snap.snap_to_grid(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); // 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 update_main_shape(&mut paths, &transforms, sd); } } } else if let Holding::Image(e, offset) = *holding { if let Ok(mut t) = transforms.get_mut(e) { t.translation = snap.snap_to_grid(mouse_pos - offset).extend(t.translation.z); } } } } pub fn calc_shape_center_offset(transforms: &Query<&mut Transform>, sd: &ShapeData) -> Vec2 { match sd.shape { CreateShape::Triangle => { assert!(sd.edges.len() == 3); let e1 = transforms.get(sd.edges[0]).unwrap().translation.xy(); let e2 = transforms.get(sd.edges[1]).unwrap().translation.xy(); let e3 = transforms.get(sd.edges[2]).unwrap().translation.xy(); (e1 + e2 + e3) / 3.0 }, CreateShape::Square => { assert!(sd.edges.len() == 2); let e1 = transforms.get(sd.edges[0]).unwrap().translation.xy(); let e2 = transforms.get(sd.edges[1]).unwrap().translation.xy(); (e1 + e2) * 0.5 }, CreateShape::Circle => { if let Ok(gt) = transforms.get(sd.center.unwrap()) { gt.translation.xy() } else { Vec2::ZERO } }, CreateShape::Capsule => unimplemented!("Capsule is disabled for now"), } } 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); assert!(sd.center.is_some()); let fp = transforms.get(sd.edges[0]).unwrap().translation.xy(); let sp = transforms.get(sd.edges[1]).unwrap().translation.xy(); let tp = transforms.get(sd.edges[2]).unwrap().translation.xy(); let shape = shapes::Polygon { points: Vec::from([fp, sp, tp]), closed: true, }; ShapePath::build_as(&shape) }, CreateShape::Square => { assert!(sd.edges.len() == 2); assert!(sd.center.is_some()); let e1 = transforms.get(sd.edges[0]).unwrap().translation.xy(); let e2 = transforms.get(sd.edges[1]).unwrap().translation.xy(); let ext = (e2 - e1).abs(); let shape = shapes::Rectangle { extents: ext, origin: RectangleOrigin::Center }; ShapePath::build_as(&shape) }, CreateShape::Circle => { assert!(sd.center.is_some()); assert!(sd.edges.len() == 1); let center= transforms.get(sd.center.unwrap()).unwrap().translation.xy(); let edge = transforms.get(sd.edges[0]).unwrap().translation.xy(); let shape = shapes::Circle { radius: (edge - center).length(), center: Vec2::ZERO }; ShapePath::build_as(&shape) }, CreateShape::Capsule => { panic!("Capsule creation not implemented yet!"); }, }; if let Ok(mut p) = paths.get_mut(sd.main_shape) { *p = path; } }