From 4373e671a0ca1327ea079975989f744c2d93a8a3 Mon Sep 17 00:00:00 2001 From: Rendo Date: Wed, 5 Aug 2026 21:15:39 +0500 Subject: [PATCH] feat: Box brush --- src/grid/manipulation.rs | 25 +++++++++-- src/view/editor.rs | 97 +++++++++++++++++++++++++++------------- src/view/ui.rs | 5 ++- 3 files changed, 90 insertions(+), 37 deletions(-) diff --git a/src/grid/manipulation.rs b/src/grid/manipulation.rs index c73e5b1..7d5a71b 100644 --- a/src/grid/manipulation.rs +++ b/src/grid/manipulation.rs @@ -15,10 +15,14 @@ impl<'a> GridManipulationTools<'a> { }; self.grid.data[index] = element; } - pub fn fill_xyz(&mut self,x1: u32, y1: u32, z1: u32, x2: u32, y2: u32, z2: u32, element: GridElement) { - if x1 > x2 || y1 > y2 || z1 > z2 { + pub fn set_vec(&mut self, vec: UVec3, element: GridElement) { + let Some(index) = self.grid.indexify(vec) else { return; - } + }; + self.grid.data[index] = element; + } + pub fn fill_xyz(&mut self,x1: u32, y1: u32, z1: u32, x2: u32, y2: u32, z2: u32, element: GridElement) { + let (x1,y1,z1,x2,y2,z2) = (x1.min(x2), y1.min(y2), z1.min(z2), x1.max(x2), y1.max(y2), z1.max(z2)); for x in x1..=x2 { for y in y1..=y2 { @@ -27,6 +31,21 @@ impl<'a> GridManipulationTools<'a> { continue; }; + self.grid.data[index] = element.clone(); + } + } + } + } + pub fn fill_vec(&mut self,vec1: UVec3, vec2: UVec3, element: GridElement) { + let (min_corner, max_corner) = (vec1.min(vec2), vec1.max(vec2)); + + for x in min_corner.x..=max_corner.x { + for y in min_corner.y..=max_corner.y { + for z in min_corner.z..=max_corner.z { + let Some(index) = self.grid.indexify(UVec3::new(x,y,z)) else { + continue; + }; + self.grid.data[index] = element.clone(); } } diff --git a/src/view/editor.rs b/src/view/editor.rs index 3e5aba7..ac9fb5d 100644 --- a/src/view/editor.rs +++ b/src/view/editor.rs @@ -15,7 +15,7 @@ impl Plugin for EditorPlugin { } } -#[derive(Default, Clone, PartialEq, Eq)] +#[derive(Default, PartialEq, Eq)] pub enum Confirmation { #[default] None, @@ -23,6 +23,32 @@ pub enum Confirmation { Confirmed(T) } +impl Confirmation { + pub fn from_option(option: &Option) -> Self { + match option { + Some(item) => Confirmation::Suggestion(item.clone()), + None => Confirmation::None, + } + } + pub fn to_option(&self) -> Option { + match self { + Confirmation::None => None, + Confirmation::Suggestion(item) => Some(item.clone()), + Confirmation::Confirmed(item) => Some(item.clone()), + } + } +} + +impl Clone for Confirmation { + fn clone(&self) -> Self { + match self { + Self::None => Self::None, + Self::Suggestion(arg0) => Self::Suggestion(arg0.clone()), + Self::Confirmed(arg0) => Self::Confirmed(arg0.clone()), + } + } +} + #[derive(Default, Clone, PartialEq, Eq,Resource)] pub struct EditorTool { pub block: GridElement, @@ -34,14 +60,16 @@ pub enum ToolType { #[default] None, Paint{ position: Option }, - //Box { first_corner: Confirmation, second_corner: Option } + Box { first_corner: Confirmation, second_corner: Option } } + impl Display for ToolType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f,"{}",match self { ToolType::None => "None", - ToolType::Paint { position: _ } => "Paint" + ToolType::Paint { position: _ } => "Paint", + ToolType::Box { first_corner: _, second_corner: _ } => "Box", }) } } @@ -51,15 +79,8 @@ impl Display for ToolType { pub struct ViewCube; #[derive(Event)] -pub struct ApplyTool{ - pub tool: EditorTool -} +pub struct ApplyTool; -impl ApplyTool { - pub fn new(tool: EditorTool) -> Self { - Self { tool } - } -} pub fn calculate_tools( mut commands: Commands, @@ -69,38 +90,50 @@ pub fn calculate_tools( mut tool: ResMut, grid: Res ) { - let tool_change: ToolType = match tool.kind { + // Calculating tile cursor tile placement + let mut cursor_position: Option = None; + + for (_, ray) in ray_map.iter() { + let Some((_, hit)) = raycast.cast_ray(*ray, &MeshRayCastSettings::default()).first() else {continue;}; + + let placement_position = if let GridElement::Air = tool.block { hit.point - 0.51 * hit.normal.normalize() } else { hit.point + 0.51 * hit.normal.normalize() }.round(); + + cursor_position = Some(placement_position.as_ivec3().max(IVec3::ZERO).as_uvec3().min(grid.size-UVec3::ONE)); + } + + let tool_change: ToolType = match tool.kind.clone() { ToolType::None => ToolType::None, - ToolType::Paint { position: _ } => { - let mut new_position: Option = None; - - for (_, ray) in ray_map.iter() { - let Some((_, hit)) = raycast.cast_ray(*ray, &MeshRayCastSettings::default()).first() else {continue;}; - - let placement_position = if let GridElement::Air = tool.block { hit.point - 0.51 * hit.normal.normalize() } else { hit.point + 0.51 * hit.normal.normalize() }.round(); - - new_position = Some(placement_position.as_ivec3().max(IVec3::ZERO).as_uvec3().min(grid.size-UVec3::ONE)); + ToolType::Paint { position: _ } => ToolType::Paint { position: cursor_position }, + ToolType::Box { first_corner, second_corner: _ } => { + if let Confirmation::Confirmed(_) = first_corner { + ToolType::Box { first_corner, second_corner: cursor_position } + } else { + ToolType::Box { first_corner: Confirmation::from_option(&cursor_position), second_corner: None } } - - ToolType::Paint { position: new_position } - }, + } }; tool.kind = tool_change; if mouse_button.just_pressed(MouseButton::Left) { - commands.trigger(ApplyTool::new(tool.clone())); + commands.trigger(ApplyTool); } } -pub fn apply_tool(params: On, mut grid: ResMut) { - match params.tool.kind { - ToolType::None => {}, - ToolType::Paint { position } => { - let Some(position) = position else { return; }; - let index = grid.indexify(position).unwrap(); - grid.data[index] = params.tool.block; +pub fn apply_tool(_params: On, mut grid: ResMut, mut tool: ResMut) { + let mut editor = grid.manipulate(); + match tool.kind { + ToolType::Paint { position: Some(position) } => { + editor.set_vec(position, tool.block); }, + ToolType::Box { first_corner: Confirmation::Suggestion(first_corner), second_corner } => { + tool.kind = ToolType::Box { first_corner: Confirmation::Confirmed(first_corner), second_corner: second_corner }; + }, + ToolType::Box { first_corner: Confirmation::Confirmed(first_corner), second_corner: Some(second_corner) } => { + editor.fill_vec(first_corner, second_corner, tool.block); + tool.kind = ToolType::Box { first_corner: Confirmation::None, second_corner: None }; + }, + _ => {}, } } diff --git a/src/view/ui.rs b/src/view/ui.rs index 1ec639d..3fb18fe 100644 --- a/src/view/ui.rs +++ b/src/view/ui.rs @@ -5,7 +5,7 @@ use bevy::prelude::*; use crate::MAX_FLUID_AMOUNT; use crate::grid::{Grid, GridElement}; use crate::simulation::SimulationStatus; -use crate::view::editor::{EditorTool,ToolType}; +use crate::view::editor::{EditorTool,ToolType,Confirmation}; pub struct UIPlugin; @@ -105,7 +105,8 @@ fn editor_tools_column() -> impl Scene { label("Tools") ToolLabel, tool_button(ToolType::None), - tool_button(ToolType::Paint{position: None}) + tool_button(ToolType::Paint{position: None}), + tool_button(ToolType::Box { first_corner: Confirmation::None, second_corner: None }) ] } }