1use bevy::prelude::{
2 debug, shape, warn, AlphaMode, Assets, Commands, Component, Entity, EventReader, EventWriter,
3 FromWorld, Handle, Local, Mesh, MouseButton, PbrBundle, Query, Res, ResMut, Resource,
4 StandardMaterial, TouchInput, Transform, Vec3, With, World,
5};
6use bevy_input::{touch::TouchPhase, Input};
7use bevy_mod_raycast::Intersection;
8
9use crate::ShapeDrawRaycastSet;
10
11#[derive(Resource)]
12pub struct BoxDrawResources {
13 pub material: Handle<StandardMaterial>,
14 pub initial_size: f32,
16 pub initial_height: f32,
18}
19
20impl FromWorld for BoxDrawResources {
21 fn from_world(world: &mut World) -> Self {
22 let world = world.cell();
23 let mut materials = world
24 .get_resource_mut::<Assets<StandardMaterial>>()
25 .unwrap();
26
27 let material = materials.add(StandardMaterial {
28 base_color: bevy::prelude::Color::rgba(
29 0x10 as f32 / 0xFF as f32,
30 0x10 as f32 / 0xFF as f32,
31 0xF0 as f32 / 0xFF as f32,
32 0.5,
33 ),
34 alpha_mode: AlphaMode::Blend,
35 ..Default::default()
36 });
37
38 Self {
39 material,
40 initial_size: 0.01,
41 initial_height: 0.2,
42 }
43 }
44}
45
46pub(crate) fn keep_enabled(
47 mut event_writer: EventWriter<DrawStateEvent>,
48 state: Res<DrawingState>,
49) {
50 if let DrawingState::Disabled = *state {
51 event_writer.send(DrawStateEvent::Enable);
52 }
53}
54
55#[derive(Copy, Clone, Debug)]
56pub enum DrawShapeEvent {
57 Spawned(Entity),
59 Redrawing(Entity),
60 Finished(Entity),
61}
62
63pub enum DrawStateEvent {
64 Enable,
65 Redraw(Entity),
67 Disable,
68}
69
70#[derive(Debug, Component)]
73pub enum Shape {
74 Box(Vec3),
75}
76
77#[derive(Resource)]
78pub(crate) enum DrawingState {
79 Idle(Option<Entity>),
80 Disabled,
81}
82
83impl Default for DrawingState {
84 fn default() -> Self {
85 Self::Disabled
86 }
87}
88
89#[derive(Component)]
90pub(crate) struct Editing(pub Vec3);
91
92pub(crate) fn draw_state(
93 mut event_reader: EventReader<DrawStateEvent>,
94 mut state: ResMut<DrawingState>,
95) {
96 for ev in event_reader.iter() {
97 match ev {
98 DrawStateEvent::Redraw(e) => *state = DrawingState::Idle(Some(*e)),
99 DrawStateEvent::Enable => *state = DrawingState::Idle(None),
100 DrawStateEvent::Disable => *state = DrawingState::Disabled,
101 }
102 }
103}
104
105#[derive(Resource, Default)]
106pub(crate) struct TouchId(Option<u64>);
107
108pub(crate) fn draw_box(
109 mut meshes: ResMut<Assets<Mesh>>,
110 query: Query<&Intersection<ShapeDrawRaycastSet>>,
111 keys: Res<Input<MouseButton>>,
112 resources: Res<BoxDrawResources>,
113 mut commands: Commands,
114 edit_box: Query<Entity, With<Editing>>,
115 shapes: Query<&Shape>,
116 mut event_writer: EventWriter<DrawShapeEvent>,
117 mut touch_events: EventReader<TouchInput>,
118 mut event_queue: Local<Vec<DrawShapeEvent>>,
119 state: Res<DrawingState>,
120 mut touch_id: ResMut<TouchId>,
121 mut touch_started: Local<bool>,
122) {
123 let mut next_event = event_queue.pop();
125 while next_event.is_some() {
126 event_writer.send(next_event.unwrap());
127 next_event = event_queue.pop();
128 }
129
130 let redraw = match *state {
131 DrawingState::Idle(e) => e,
132 _ => return,
133 };
134
135 let height = if let Some(e) = redraw {
136 let shape = shapes.get(e);
137 if let Ok(Shape::Box(dim)) = shape {
138 dim.y
139 } else {
140 resources.initial_height
141 }
142 } else {
143 resources.initial_height
144 };
145
146 let mut started = keys.just_pressed(MouseButton::Left);
147 let mut ended = keys.just_released(MouseButton::Left);
148
149 for ev in touch_events.iter() {
150 if let Some(id) = touch_id.0 {
151 if id != ev.id {
152 continue;
153 }
154 }
155
156 match ev.phase {
157 TouchPhase::Started => {
158 touch_id.0 = Some(ev.id);
159 }
160 TouchPhase::Ended | TouchPhase::Cancelled => {
161 if touch_id.0.is_some() {
162 ended = true;
163 *touch_started = false;
164 touch_id.0 = None;
165 }
166 }
167 TouchPhase::Moved => {
168 started = !*touch_started;
169 *touch_started = true;
170 }
171 }
172 }
173 let intersect_position = get_closest_intersection(query);
174
175 if started {
176 if let Some(intersect_position) = intersect_position {
178 let mut transform = Transform::default();
179 transform.translation = intersect_position
180 + Vec3::new(
181 resources.initial_size / 2.,
182 height / 2.,
183 resources.initial_size / 2.,
184 );
185 let origin: Vec3 = intersect_position;
186
187 let mesh = meshes.add(Mesh::from(shape::Box::new(
188 resources.initial_size,
189 height,
190 resources.initial_size,
191 )));
192
193 let new_drawing = redraw.is_none();
194
195 let mut e_commands = match redraw {
196 Some(e) => commands.entity(e),
197 None => commands.spawn(PbrBundle {
198 mesh: mesh.clone(),
199 material: resources.material.clone(),
200 transform,
201 ..Default::default()
202 }),
203 };
204
205 let e = e_commands
206 .insert(Editing(origin))
207 .insert(Shape::Box(Vec3::new(
208 resources.initial_size,
209 height,
210 resources.initial_size,
211 )))
212 .id();
213
214 if new_drawing {
215 event_queue.push(DrawShapeEvent::Spawned(e));
216 } else {
217 event_queue.push(DrawShapeEvent::Redrawing(e));
218 }
219 }
220 } else if ended {
221 if let Ok(e) = edit_box.get_single() {
222 commands.entity(e).remove::<Editing>();
223 event_queue.push(DrawShapeEvent::Finished(e));
224 }
225 }
226}
227
228fn get_closest_intersection<'a>(
229 query: Query<&'a Intersection<ShapeDrawRaycastSet>>,
230) -> Option<Vec3> {
231 let mut intersect_position = None;
232 let mut distance = f32::INFINITY;
234 for intersection in &query {
235 debug!(
236 "Distance {:?}, Position {:?}",
237 intersection.distance(),
238 intersection.position()
239 );
240 if let (Some(dist), Some(pos)) = (intersection.distance(), intersection.position()) {
242 if dist < distance {
243 distance = dist;
244 intersect_position = Some(*pos);
245 }
246 }
247 }
248 intersect_position
249}
250
251pub(crate) fn edit_box(
252 mut e_box: Query<(&Handle<Mesh>, &mut Transform, &Editing, &mut Shape)>,
253 query: Query<&Intersection<ShapeDrawRaycastSet>>,
254 keys: Res<Input<MouseButton>>,
255 mut meshes: ResMut<Assets<Mesh>>,
256 state: Res<DrawingState>,
257 mut touch_events: EventReader<TouchInput>,
258 touch_id: Res<TouchId>,
259) {
260 match *state {
261 DrawingState::Disabled => return,
262 _ => {}
263 }
264
265 let mut update = keys.pressed(MouseButton::Left);
266
267 for ev in touch_events.iter() {
268 if let Some(id) = touch_id.0 {
269 if id != ev.id {
270 warn!("Wrong touch id");
271 continue;
272 }
273 }
274
275 update = true;
276 break;
277 }
278
279 if update {
280 if let Ok((handle, mut transform, edit_origin, mut shape)) = e_box.get_single_mut() {
281 if let Some(mesh) = meshes.get_mut(handle) {
282 let mut opposite = Vec3::default();
283
284 for intersection in &query {
285 if let Some(pos) = intersection.position() {
286 opposite = *pos;
287 }
288 }
289
290 if opposite == Vec3::ZERO || opposite == edit_origin.0 {
291 return;
292 }
293
294 let p1 = edit_origin.0;
295 let p2 = opposite;
296
297 let dx = p2.x - p1.x;
298 let dz = p2.z - p1.z;
299
300 let x = dx.abs();
301 let z = dz.abs();
302
303 let height = match &mut *shape {
304 Shape::Box(size) => {
305 size.x = x;
306 size.z = z;
307 size.y
308 }
309 };
310
311 let b = shape::Box::new(x, height, z);
312
313 debug!("Box: {:?}", b);
314
315 *mesh = Mesh::from(b);
316 transform.translation.x = p2.x - (dx / 2.0);
317 transform.translation.z = p2.z - (dz / 2.0);
318 transform.translation.y = opposite.y + (height / 2.0);
319 }
320 } else {
321 warn!("No editbox found");
324 }
325 }
326}