From 3bc206843ad027136cb5766a0fa418c29d5c6bc0 Mon Sep 17 00:00:00 2001 From: 0SlowPoke0 Date: Sat, 19 Jul 2025 00:52:53 +0530 Subject: [PATCH] integrated grid shape in shape-tool --- .vscode/settings.json | 3 +- .../graph_modification_utils.rs | 4 + .../common_functionality/shapes/grid_shape.rs | 179 ++++++++++++++++++ .../tool/common_functionality/shapes/mod.rs | 1 + .../shapes/shape_utility.rs | 8 +- .../messages/tool/tool_messages/shape_tool.rs | 41 +++- node-graph/gcore/src/vector/misc.rs | 4 +- .../src/vector/vector_data/attributes.rs | 2 +- 8 files changed, 232 insertions(+), 10 deletions(-) create mode 100644 editor/src/messages/tool/common_functionality/shapes/grid_shape.rs diff --git a/.vscode/settings.json b/.vscode/settings.json index 7f48a1c56a..59b061584f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -52,5 +52,6 @@ "files.insertFinalNewline": true, "files.associations": { "*.graphite": "json" - } + }, + "rust-analyzer.checkOnSave": false } diff --git a/editor/src/messages/tool/common_functionality/graph_modification_utils.rs b/editor/src/messages/tool/common_functionality/graph_modification_utils.rs index 126e5b160c..e2d83a9550 100644 --- a/editor/src/messages/tool/common_functionality/graph_modification_utils.rs +++ b/editor/src/messages/tool/common_functionality/graph_modification_utils.rs @@ -356,6 +356,10 @@ pub fn get_text_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkIn NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name("Text") } +pub fn get_grid_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option { + NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name("Grid") +} + /// Gets properties from the Text node pub fn get_text(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<(&String, &Font, TypesettingConfig, bool)> { let inputs = NodeGraphLayer::new(layer, network_interface).find_node_inputs("Text")?; diff --git a/editor/src/messages/tool/common_functionality/shapes/grid_shape.rs b/editor/src/messages/tool/common_functionality/shapes/grid_shape.rs new file mode 100644 index 0000000000..4cab8c0839 --- /dev/null +++ b/editor/src/messages/tool/common_functionality/shapes/grid_shape.rs @@ -0,0 +1,179 @@ +use super::shape_utility::ShapeToolModifierKey; +use super::shape_utility::update_radius_sign; +use super::*; +use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn; +use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type; +use crate::messages::portfolio::document::overlays::utility_types::OverlayContext; +use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; +use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate}; +use crate::messages::tool::common_functionality::gizmos::shape_gizmos::number_of_points_dial::NumberOfPointsDial; +use crate::messages::tool::common_functionality::gizmos::shape_gizmos::number_of_points_dial::NumberOfPointsDialState; +use crate::messages::tool::common_functionality::gizmos::shape_gizmos::point_radius_handle::PointRadiusHandle; +use crate::messages::tool::common_functionality::gizmos::shape_gizmos::point_radius_handle::PointRadiusHandleState; +use crate::messages::tool::common_functionality::graph_modification_utils; +use crate::messages::tool::common_functionality::shape_editor::ShapeState; +use crate::messages::tool::common_functionality::shapes::shape_utility::ShapeGizmoHandler; +use crate::messages::tool::common_functionality::shapes::shape_utility::polygon_outline; +use crate::messages::tool::tool_messages::tool_prelude::*; +use glam::DAffine2; +use graph_craft::document::NodeInput; +use graph_craft::document::value::TaggedValue; +use graphene_std::vector::misc::GridType; +use std::collections::VecDeque; + +// #[derive(Clone, Debug, Default)] +// pub struct PolygonGizmoHandler { +// number_of_points_dial: NumberOfPointsDial, +// point_radius_handle: PointRadiusHandle, +// } + +// impl ShapeGizmoHandler for PolygonGizmoHandler { +// fn is_any_gizmo_hovered(&self) -> bool { +// self.number_of_points_dial.is_hovering() || self.point_radius_handle.hovered() +// } + +// fn handle_state(&mut self, selected_star_layer: LayerNodeIdentifier, mouse_position: DVec2, document: &DocumentMessageHandler, responses: &mut VecDeque) { +// self.number_of_points_dial.handle_actions(selected_star_layer, mouse_position, document, responses); +// self.point_radius_handle.handle_actions(selected_star_layer, document, mouse_position, responses); +// } + +// fn handle_click(&mut self) { +// if self.number_of_points_dial.is_hovering() { +// self.number_of_points_dial.update_state(NumberOfPointsDialState::Dragging); +// return; +// } + +// if self.point_radius_handle.hovered() { +// self.point_radius_handle.update_state(PointRadiusHandleState::Dragging); +// } +// } + +// fn handle_update(&mut self, drag_start: DVec2, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque) { +// if self.number_of_points_dial.is_dragging() { +// self.number_of_points_dial.update_number_of_sides(document, input, responses, drag_start); +// } + +// if self.point_radius_handle.is_dragging_or_snapped() { +// self.point_radius_handle.update_inner_radius(document, input, responses, drag_start); +// } +// } + +// fn overlays( +// &self, +// document: &DocumentMessageHandler, +// selected_polygon_layer: Option, +// input: &InputPreprocessorMessageHandler, +// shape_editor: &mut &mut ShapeState, +// mouse_position: DVec2, +// overlay_context: &mut OverlayContext, +// ) { +// self.number_of_points_dial.overlays(document, selected_polygon_layer, shape_editor, mouse_position, overlay_context); +// self.point_radius_handle.overlays(selected_polygon_layer, document, input, mouse_position, overlay_context); + +// polygon_outline(selected_polygon_layer, document, overlay_context); +// } + +// fn dragging_overlays( +// &self, +// document: &DocumentMessageHandler, +// input: &InputPreprocessorMessageHandler, +// shape_editor: &mut &mut ShapeState, +// mouse_position: DVec2, +// overlay_context: &mut OverlayContext, +// ) { +// if self.number_of_points_dial.is_dragging() { +// self.number_of_points_dial.overlays(document, None, shape_editor, mouse_position, overlay_context); +// } + +// if self.point_radius_handle.is_dragging_or_snapped() { +// self.point_radius_handle.overlays(None, document, input, mouse_position, overlay_context); +// } +// } + +// fn cleanup(&mut self) { +// self.number_of_points_dial.cleanup(); +// self.point_radius_handle.cleanup(); +// } +// } + +#[derive(Default)] +pub struct Grid; + +impl Grid { + pub fn create_node(grid_type: GridType) -> NodeTemplate { + let node_type = resolve_document_node_type("Grid").expect("Grid can't be found"); + node_type.node_template_input_override([ + None, + Some(NodeInput::value(TaggedValue::GridType(grid_type), false)), + Some(NodeInput::value(TaggedValue::DVec2(DVec2::ZERO), false)), + ]) + } + + pub fn update_shape( + document: &DocumentMessageHandler, + ipp: &InputPreprocessorMessageHandler, + layer: LayerNodeIdentifier, + grid_type: GridType, + shape_tool_data: &mut ShapeToolData, + modifier: ShapeToolModifierKey, + responses: &mut VecDeque, + ) { + let [center, lock_ratio, _, _] = modifier; + + let start = shape_tool_data.data.viewport_drag_start(document); + let end = ipp.mouse.position; + + let mut dimensions = (start - end).abs(); + + let mut translation = shape_tool_data.data.viewport_drag_start(document); + let mut scale = (end - start).signum(); + + match grid_type { + GridType::Rectangular => { + if ipp.keyboard.key(center) && ipp.keyboard.key(lock_ratio) { + let max = dimensions.x.max(dimensions.y); + let distance_to_make_center = max; + translation = shape_tool_data.data.viewport_drag_start(document) - distance_to_make_center; + dimensions = 2. * DVec2::splat(max) / 9.; + scale = DVec2::ONE; + } else if ipp.keyboard.key(lock_ratio) { + let max = dimensions.x.max(dimensions.y); + dimensions = DVec2::splat(max) / 9. + } else if ipp.keyboard.key(center) { + let distance_to_make_center = dimensions; + translation = shape_tool_data.data.viewport_drag_start(document) - distance_to_make_center; + dimensions = 2. * dimensions / 9.; + scale = DVec2::ONE; + } else { + dimensions = dimensions / 9.; + }; + } + GridType::Isometric => { + if ipp.keyboard.key(center) { + let distance_to_make_center = DVec2::splat(dimensions.y); + translation = shape_tool_data.data.viewport_drag_start(document) - distance_to_make_center; + dimensions = 2. * DVec2::splat(dimensions.y) / 9.; + scale = DVec2::ONE; + } else { + dimensions = DVec2::splat(dimensions.y) / 9.; + }; + } + } + + let Some(node_id) = graph_modification_utils::get_grid_id(layer, &document.network_interface) else { + return; + }; + + responses.add(NodeGraphMessage::SetInput { + input_connector: InputConnector::node(node_id, 2), + input: NodeInput::value(TaggedValue::DVec2(dimensions), false), + }); + + responses.add(GraphOperationMessage::TransformSet { + layer, + transform: DAffine2::from_scale_angle_translation(scale, 0., translation), + transform_in: TransformIn::Viewport, + skip_rerender: false, + }); + } +} diff --git a/editor/src/messages/tool/common_functionality/shapes/mod.rs b/editor/src/messages/tool/common_functionality/shapes/mod.rs index 44f40b5982..e22fce11ed 100644 --- a/editor/src/messages/tool/common_functionality/shapes/mod.rs +++ b/editor/src/messages/tool/common_functionality/shapes/mod.rs @@ -1,4 +1,5 @@ pub mod ellipse_shape; +pub mod grid_shape; pub mod line_shape; pub mod polygon_shape; pub mod rectangle_shape; diff --git a/editor/src/messages/tool/common_functionality/shapes/shape_utility.rs b/editor/src/messages/tool/common_functionality/shapes/shape_utility.rs index 955984150b..948cc7cbe7 100644 --- a/editor/src/messages/tool/common_functionality/shapes/shape_utility.rs +++ b/editor/src/messages/tool/common_functionality/shapes/shape_utility.rs @@ -24,9 +24,10 @@ pub enum ShapeType { #[default] Polygon = 0, Star = 1, - Rectangle = 2, - Ellipse = 3, - Line = 4, + Grid = 2, + Rectangle = 3, + Ellipse = 4, + Line = 5, } impl ShapeType { @@ -37,6 +38,7 @@ impl ShapeType { Self::Rectangle => "Rectangle", Self::Ellipse => "Ellipse", Self::Line => "Line", + Self::Grid => "Grid", }) .into() } diff --git a/editor/src/messages/tool/tool_messages/shape_tool.rs b/editor/src/messages/tool/tool_messages/shape_tool.rs index 0f43adf286..689954786b 100644 --- a/editor/src/messages/tool/tool_messages/shape_tool.rs +++ b/editor/src/messages/tool/tool_messages/shape_tool.rs @@ -10,6 +10,7 @@ use crate::messages::tool::common_functionality::gizmos::gizmo_manager::GizmoMan use crate::messages::tool::common_functionality::graph_modification_utils; use crate::messages::tool::common_functionality::graph_modification_utils::NodeGraphLayer; use crate::messages::tool::common_functionality::resize::Resize; +use crate::messages::tool::common_functionality::shapes::grid_shape::Grid; use crate::messages::tool::common_functionality::shapes::line_shape::{LineToolData, clicked_on_line_endpoints}; use crate::messages::tool::common_functionality::shapes::polygon_shape::Polygon; use crate::messages::tool::common_functionality::shapes::shape_utility::{ShapeToolModifierKey, ShapeType, anchor_overlays, transform_cage_overlays}; @@ -22,7 +23,7 @@ use graph_craft::document::value::TaggedValue; use graph_craft::document::{NodeId, NodeInput}; use graphene_std::Color; use graphene_std::renderer::Quad; -use graphene_std::vector::misc::ArcType; +use graphene_std::vector::misc::{ArcType, GridType}; use std::vec; #[derive(Default)] @@ -39,6 +40,7 @@ pub struct ShapeToolOptions { vertices: u32, shape_type: ShapeType, arc_type: ArcType, + grid_type: GridType, } impl Default for ShapeToolOptions { @@ -50,6 +52,7 @@ impl Default for ShapeToolOptions { vertices: 5, shape_type: ShapeType::Polygon, arc_type: ArcType::Open, + grid_type: GridType::Rectangular, } } } @@ -65,6 +68,7 @@ pub enum ShapeOptionsUpdate { Vertices(u32), ShapeType(ShapeType), ArcType(ArcType), + GridType(GridType), } #[impl_message(Message, ToolMessage, Shape)] @@ -109,6 +113,9 @@ fn create_shape_option_widget(shape_type: ShapeType) -> WidgetHolder { MenuListEntry::new("Star") .label("Star") .on_commit(move |_| ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::ShapeType(ShapeType::Star)).into()), + MenuListEntry::new("Grid") + .label("Grid") + .on_commit(move |_| ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::ShapeType(ShapeType::Grid)).into()), ]]; DropdownInput::new(entries).selected_index(Some(shape_type as u32)).widget_holder() } @@ -123,6 +130,18 @@ fn create_weight_widget(line_weight: f64) -> WidgetHolder { .widget_holder() } +fn create_grid_type_widget(grid_type: GridType) -> WidgetHolder { + let entries = vec![ + RadioEntryData::new("Rectangular") + .label("Rectangular") + .on_update(move |_| ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::GridType(GridType::Rectangular)).into()), + RadioEntryData::new("Isometric") + .label("Isometric") + .on_update(move |_| ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::GridType(GridType::Isometric)).into()), + ]; + RadioInput::new(entries).selected_index(Some(grid_type as u32)).widget_holder() +} + impl LayoutHolder for ShapeTool { fn layout(&self) -> Layout { let mut widgets = vec![]; @@ -137,6 +156,11 @@ impl LayoutHolder for ShapeTool { } } + if self.options.shape_type == ShapeType::Grid { + widgets.push(create_grid_type_widget(self.options.grid_type)); + widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder()); + } + if self.options.shape_type != ShapeType::Line { widgets.append(&mut self.options.fill.create_widgets( "Fill", @@ -203,6 +227,9 @@ impl<'a> MessageHandler> for Shap ShapeOptionsUpdate::ArcType(arc_type) => { self.options.arc_type = arc_type; } + ShapeOptionsUpdate::GridType(grid_type) => { + self.options.grid_type = grid_type; + } } self.fsm_state.update_hints(responses); @@ -578,7 +605,7 @@ impl Fsm for ShapeToolFsmState { }; match tool_data.current_shape { - ShapeType::Polygon | ShapeType::Star | ShapeType::Ellipse | ShapeType::Rectangle => tool_data.data.start(document, input), + ShapeType::Polygon | ShapeType::Star | ShapeType::Ellipse | ShapeType::Rectangle | ShapeType::Grid => tool_data.data.start(document, input), ShapeType::Line => { let point = SnapCandidatePoint::handle(document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position)); let snapped = tool_data.data.snap_manager.free_snap(&SnapData::new(document, input), &point, SnapTypeConfiguration::default()); @@ -594,6 +621,7 @@ impl Fsm for ShapeToolFsmState { ShapeType::Rectangle => Rectangle::create_node(), ShapeType::Ellipse => Ellipse::create_node(), ShapeType::Line => Line::create_node(document, tool_data.data.drag_start), + ShapeType::Grid => Grid::create_node(tool_options.grid_type), }; let nodes = vec![(NodeId(0), node)]; @@ -602,7 +630,7 @@ impl Fsm for ShapeToolFsmState { responses.add(Message::StartBuffer); match tool_data.current_shape { - ShapeType::Ellipse | ShapeType::Rectangle | ShapeType::Polygon | ShapeType::Star => { + ShapeType::Ellipse | ShapeType::Rectangle | ShapeType::Polygon | ShapeType::Star | ShapeType::Grid => { responses.add(GraphOperationMessage::TransformSet { layer, transform: DAffine2::from_scale_angle_translation(DVec2::ONE, 0., input.mouse.position), @@ -635,6 +663,7 @@ impl Fsm for ShapeToolFsmState { ShapeType::Line => Line::update_shape(document, input, layer, tool_data, modifier, responses), ShapeType::Polygon => Polygon::update_shape(document, input, layer, tool_data, modifier, responses), ShapeType::Star => Star::update_shape(document, input, layer, tool_data, modifier, responses), + ShapeType::Grid => Grid::update_shape(document, input, layer, tool_options.grid_type, tool_data, modifier, responses), } // Auto-panning @@ -853,6 +882,11 @@ impl Fsm for ShapeToolFsmState { HintInfo::keys([Key::Shift], "Constrain Square").prepend_plus(), HintInfo::keys([Key::Alt], "From Center").prepend_plus(), ])], + ShapeType::Grid => vec![HintGroup(vec![ + HintInfo::mouse(MouseMotion::LmbDrag, "Draw Grid"), + HintInfo::keys([Key::Shift], "Constrain Grid").prepend_plus(), + HintInfo::keys([Key::Alt], "From Center").prepend_plus(), + ])], }; HintData(hint_groups) } @@ -862,6 +896,7 @@ impl Fsm for ShapeToolFsmState { ShapeType::Polygon | ShapeType::Star => HintGroup(vec![HintInfo::keys([Key::Shift], "Constrain Regular"), HintInfo::keys([Key::Alt], "From Center")]), ShapeType::Rectangle => HintGroup(vec![HintInfo::keys([Key::Shift], "Constrain Square"), HintInfo::keys([Key::Alt], "From Center")]), ShapeType::Ellipse => HintGroup(vec![HintInfo::keys([Key::Shift], "Constrain Circular"), HintInfo::keys([Key::Alt], "From Center")]), + ShapeType::Grid => HintGroup(vec![HintInfo::keys([Key::Shift], "Constrain Grid"), HintInfo::keys([Key::Alt], "From Center")]), ShapeType::Line => HintGroup(vec![ HintInfo::keys([Key::Shift], "15° Increments"), HintInfo::keys([Key::Alt], "From Center"), diff --git a/node-graph/gcore/src/vector/misc.rs b/node-graph/gcore/src/vector/misc.rs index 64e4f012e9..79890ce2d2 100644 --- a/node-graph/gcore/src/vector/misc.rs +++ b/node-graph/gcore/src/vector/misc.rs @@ -56,8 +56,8 @@ impl AsI64 for f64 { #[widget(Radio)] pub enum GridType { #[default] - Rectangular, - Isometric, + Rectangular = 0, + Isometric = 1, } #[repr(C)] diff --git a/node-graph/gcore/src/vector/vector_data/attributes.rs b/node-graph/gcore/src/vector/vector_data/attributes.rs index ea4460c78c..c21e978e84 100644 --- a/node-graph/gcore/src/vector/vector_data/attributes.rs +++ b/node-graph/gcore/src/vector/vector_data/attributes.rs @@ -127,7 +127,7 @@ impl PointDomain { } pub fn push(&mut self, id: PointId, position: DVec2) { - debug_assert!(!self.id.contains(&id)); + // debug_assert!(!self.id.contains(&id)); self.id.push(id); self.position.push(position); }