shape_maker/src/create.rs

332 lines
11 KiB
Rust

use bevy::math::Vec3Swizzles;
use bevy::prelude::*;
use crate::*;
use bevy_prototype_lyon::prelude::*;
pub fn create_sys(
mut coms: Commands,
p_size: Res<PointSize>,
state: Res<UiState>,
mouse: Res<Input<MouseButton>>,
wnds: Res<Windows>,
mut shape: Local<Option<ShapeData>>,
q_cam: Query<(&Camera, &GlobalTransform), With<MainCamera>>,
mut paths: Query<&mut Path>,
mut transforms: Query<&mut Transform>,
snap: Res<SnapGrid>,
) {
let mouse_pos = get_mouse_pos(&q_cam, &wnds);
if let Some(mouse_pos) = mouse_pos {
let mouse_pos = snap.snap_to_grid(mouse_pos);
if let Some(sd) = &mut *shape {
update_main_shape_creation(&mut paths, &transforms, sd, mouse_pos);
if mouse.just_released(MouseButton::Left) {
let done = insert_edge_to_shape_creation(&mut coms, sd, mouse_pos, p_size.0);
if done {
let center = add_center_point(&mut coms, sd, &transforms, mouse_pos, p_size.0);
// Update each point to be relative to the new center!
if let Ok(mut t) = transforms.get_mut(sd.main_shape) {
t.translation = center.extend(0.0);
}
if let Ok(mut t) = transforms.get_mut(sd.center.unwrap()) {
t.translation = Vec3::new(0.0, 0.0, 0.5); // Just to make sure the center is centered
}
for edge in sd.edges.iter() {
if let Ok(mut t) = transforms.get_mut(*edge) {
t.translation -= center.extend(0.0);
}
else {
// If we dont have a transform in the query for the relevant entity,
// then it should be the entity we just inserted, and we can just override its transform
coms.entity(*edge)
.insert(Transform::from_translation((mouse_pos - center).extend(0.5)));
}
}
finalize_shape_redraw(&mut paths, &transforms, sd, mouse_pos - center);
let mut s = shape.take().unwrap();
s.name = format!("{:?} {}", s.shape, s.main_shape.id());
let mut ms = coms.entity(s.main_shape);
ms.add_child(s.center.unwrap());
for e in s.edges.iter() {
ms.add_child(*e);
}
ms.insert(s);
*shape = None;
}
}
else if mouse.just_released(MouseButton::Right) {
if let Some(e) = &sd.center {
coms.entity(*e).despawn();
}
coms.entity(sd.main_shape).despawn();
sd.edges.iter().for_each(|e| coms.entity(*e).despawn());
*shape = None;
}
}
else if mouse.just_released(MouseButton::Left) {
// We can now spawn a shape in the current mouse position...
// Spawn the first point
*shape = Some(create_new_shape(&mut coms, mouse_pos, state.create_shape, p_size.0));
}
}
}
/// lpos - should be in local coordinates!
fn finalize_shape_redraw(
paths: &mut Query<&mut Path>,
transforms: &Query<&mut Transform>,
sd: &ShapeData,
lpos: Vec2, // Last position(1 we just inserted)
) {
let path = match sd.shape {
CreateShape::Triangle => {
assert!(sd.edges.len() == 3);
let fp = transforms.get(sd.edges[0]).unwrap().translation.xy();
let sp = transforms.get(sd.edges[1]).unwrap().translation.xy();
let shape = shapes::Polygon {
points: Vec::from([fp, sp, lpos]),
closed: true,
};
ShapePath::build_as(&shape)
},
CreateShape::Square => {
assert!(sd.edges.len() == 2);
let opposite_point = transforms.get(sd.edges[0]).unwrap().translation.xy();
let ext = (lpos - opposite_point).abs();
let shape = shapes::Rectangle { extents: ext, origin: RectangleOrigin::Center };
ShapePath::build_as(&shape)
},
CreateShape::Circle => {
assert!(sd.center.is_some());
let center= transforms.get(sd.center.unwrap()).unwrap().translation.xy();
let shape = shapes::Circle { radius: (lpos - 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;
}
}
/// Adds a center point to the shape, returning the center location(due to commands not being run until end of frame)
fn add_center_point(
coms: &mut Commands,
sd: &mut ShapeData,
transforms: &Query<&mut Transform>,
l_pos: Vec2, // We call this function before commands are executed, so we dont have the last entity YET!
p_size: f32,
) -> Vec2 {
if sd.center.is_some() {
return transforms.get(sd.center.unwrap()).unwrap().translation.xy();
}
let center = match sd.shape {
CreateShape::Triangle => {
assert!(sd.edges.len() == 3);
let fp = transforms.get(sd.edges[0]).unwrap().translation.xy();
let sp = transforms.get(sd.edges[1]).unwrap().translation.xy();
(fp + sp + l_pos) / 3.0
},
CreateShape::Square => {
assert!(sd.edges.len() == 2);
let e1 = transforms.get(sd.edges[0]).unwrap().translation.xy();
(e1 + l_pos) * 0.5
},
_ => panic!("Should not be reachable, as circle has a center and capsule is disabled for now!"),
};
let shape = shapes::Circle {
radius: p_size,
center: Vec2::ZERO,
};
let np = coms.spawn_bundle(GeometryBuilder::build_as(
&shape,
DrawMode::Fill(FillMode::color(Color::RED)),
Transform::from_translation(Vec3::new(0.0, 0.0, 0.5))
))
.insert(Visibility { is_visible: false })
.id();
sd.center = Some(np);
center
}
/// Inserts a new edge/point to the currently created shape, return if the shape is complete
fn insert_edge_to_shape_creation(
coms: &mut Commands,
sd: &mut ShapeData,
mpos: Vec2,
p_size: f32,
) -> bool {
// Spawn the new point
// Spawn the first point(where the user just clicked)
let shape = shapes::Circle {
radius: p_size,
center: Vec2::ZERO,
};
let np = coms.spawn_bundle(GeometryBuilder::build_as(
&shape,
DrawMode::Fill(FillMode::color(Color::RED)),
Transform::from_translation(mpos.extend(0.5))
))
.id();
match sd.shape {
CreateShape::Triangle => {
sd.edges.push(np);
sd.edges.len() == 3
},
CreateShape::Square => {
sd.edges.push(np);
sd.edges.len() == 2
},
CreateShape::Circle => {
sd.edges.push(np);
sd.edges.len() == 1
},
CreateShape::Capsule => {
if sd.edges.len() == 1 {
sd.edges.push(np);
false
}
else if sd.edges.len() == 2 {
sd.center = Some(np);
true
}
else {
panic!("Capsule was started without any edges... WHAT!");
}
},
}
}
fn update_main_shape_creation(
paths: &mut Query<&mut Path>,
transforms: &Query<&mut Transform>,
sd: &ShapeData,
mpos: Vec2,
) {
let path = match sd.shape {
CreateShape::Triangle => {
if sd.edges.len() == 1 {
let first_point = transforms.get(sd.edges[0]).unwrap().translation.xy();
let shape = shapes::Line(first_point, mpos);
ShapePath::build_as(&shape)
}
else if sd.edges.len() == 2 {
let fp = transforms.get(sd.edges[0]).unwrap().translation.xy();
let sp = transforms.get(sd.edges[1]).unwrap().translation.xy();
let shape = shapes::Polygon {
points: Vec::from([fp, sp, mpos]),
closed: true,
};
ShapePath::build_as(&shape)
}
else {
panic!("Triangle cannot have less than 1 edges or more than 2 during creation!: {:?}", sd);
}
},
CreateShape::Square => {
assert!(sd.edges.len() == 1);
let opposite_point = transforms.get(sd.edges[0]).unwrap().translation.xy();
let ext = (mpos - opposite_point).abs();
let center = mpos.min(opposite_point) + ext * 0.5;
let shape = shapes::Rectangle { extents: ext, origin: RectangleOrigin::CustomCenter(center) };
ShapePath::build_as(&shape)
},
CreateShape::Circle => {
assert!(sd.center.is_some());
let center= transforms.get(sd.center.unwrap()).unwrap().translation.xy();
let shape = shapes::Circle { radius: (mpos - center).length(), center };
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;
}
}
fn create_new_shape(coms: &mut Commands, pos: Vec2, create_shape: CreateShape, p_size: f32) -> ShapeData {
// Shape draw mode...
let draw_mode = DrawMode::Outlined {
fill_mode: FillMode::color(Color::rgba(0.0, 0.5, 0.5, 0.4)),
outline_mode: StrokeMode::new(Color::rgba(0.0, 0.5, 0.5, 0.6), 3.0),
};
// Spawn the first point(where the user just clicked)
let shape = shapes::Circle {
radius: p_size,
center: Vec2::ZERO,
};
let fp = coms.spawn_bundle(GeometryBuilder::build_as(
&shape,
DrawMode::Fill(FillMode::color(Color::RED)),
Transform::from_translation(pos.extend(0.5))
))
.id();
// Spawn main shape!
let main_shape = match create_shape {
CreateShape::Triangle | CreateShape::Capsule => {
let shape = shapes::Line(pos, pos);
coms.spawn_bundle(GeometryBuilder::build_as(
&shape,
draw_mode,
Transform::from_translation(Vec3::ZERO)
)).id()
},
CreateShape::Square => {
let shape = shapes::Rectangle { extents: Vec2::ZERO, origin: RectangleOrigin::CustomCenter(pos)};
coms.spawn_bundle(GeometryBuilder::build_as(
&shape,
draw_mode,
Transform::from_translation(Vec3::ZERO)
)).id()
},
CreateShape::Circle => {
let shape = shapes::Circle { radius: 0.0, center: pos };
coms.spawn_bundle(GeometryBuilder::build_as(
&shape,
draw_mode,
Transform::from_translation(Vec3::ZERO)
)).id()
},
};
// Get center point(if exists)
let center = match create_shape {
CreateShape::Circle => Some(fp),
_ => None,
};
let edges = if center.is_none() { Vec::from([fp]) } else { Vec::new() };
ShapeData { shape: create_shape, main_shape, edges, center, name: "shape".to_string(), note: String::new() }
}