shape_maker/src/modify.rs

219 lines
9.1 KiB
Rust

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<Holding>,
mut held_from: Local<Vec2>,
mut undo: ResMut<undo::UndoStack>,
mouse: Res<Input<MouseButton>>,
wnds: Res<Windows>,
scale: Query<&OrthographicProjection, With<MainCamera>>,
p_size: Res<PointSize>,
assets: Res<Assets<Image>>,
mut paths: Query<&mut Path>,
mut transforms: Query<&mut Transform>,
gtransforms: Query<&GlobalTransform>,
q_cam: Query<(&Camera, &GlobalTransform), With<MainCamera>>,
shapes: Query<(Entity, &ShapeData)>,
images: Query<(Entity, &Handle<Image>)>,
snap: Res<SnapGrid>,
) {
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::<ShapeData>(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::<ShapeData>(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;
}
}