From 91a5c880906ecdf853f4fe8002cd30793bfca854 Mon Sep 17 00:00:00 2001 From: 0SlowPoke0 Date: Thu, 3 Jul 2025 16:52:26 +0530 Subject: [PATCH] add spiral shape in shape tool --- .../document/node_graph/node_properties.rs | 16 +- .../graph_modification_utils.rs | 4 + .../tool/common_functionality/shapes/mod.rs | 1 + .../shapes/polygon_shape.rs | 35 ++++ .../shapes/shape_utility.rs | 8 +- .../shapes/spiral_shape.rs | 121 ++++++++++++++ .../messages/tool/tool_messages/shape_tool.rs | 155 +++++++++++------- .../gcore/src/vector/generator_nodes.rs | 2 +- 8 files changed, 272 insertions(+), 70 deletions(-) create mode 100644 editor/src/messages/tool/common_functionality/shapes/spiral_shape.rs diff --git a/editor/src/messages/portfolio/document/node_graph/node_properties.rs b/editor/src/messages/portfolio/document/node_graph/node_properties.rs index 1ecaecb005..de7cb5507d 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_properties.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_properties.rs @@ -1251,11 +1251,17 @@ pub(crate) fn spiral_properties(node_id: NodeId, context: &mut NodePropertiesCon match spiral_type { SpiralType::Archimedean => { let start_radius = LayoutGroup::Row { - widgets: number_widget(ParameterWidgetsInfo::from_index(document_node, node_id, InnerRadiusInput::INDEX, true, context), NumberInput::default()), + widgets: number_widget( + ParameterWidgetsInfo::from_index(document_node, node_id, InnerRadiusInput::INDEX, true, context), + NumberInput::default().min(0.01), + ), }; let tightness = LayoutGroup::Row { - widgets: number_widget(ParameterWidgetsInfo::from_index(document_node, node_id, TightnessInput::INDEX, true, context), NumberInput::default()), + widgets: number_widget( + ParameterWidgetsInfo::from_index(document_node, node_id, TightnessInput::INDEX, true, context), + NumberInput::default().unit(" px"), + ), }; widgets.extend([start_radius, tightness]); @@ -1264,14 +1270,14 @@ pub(crate) fn spiral_properties(node_id: NodeId, context: &mut NodePropertiesCon let start_radius = LayoutGroup::Row { widgets: number_widget( ParameterWidgetsInfo::from_index(document_node, node_id, StartRadiusInput::INDEX, true, context), - NumberInput::default().min(0.1), + NumberInput::default().min(0.001), ), }; let growth = LayoutGroup::Row { widgets: number_widget( ParameterWidgetsInfo::from_index(document_node, node_id, GrowthInput::INDEX, true, context), - NumberInput::default().max(1.).min(0.1).increment_behavior(NumberInputIncrementBehavior::Add).increment_step(0.02), + NumberInput::default().max(0.5).min(0.1).increment_behavior(NumberInputIncrementBehavior::Add).increment_step(0.01), ), }; @@ -1286,7 +1292,7 @@ pub(crate) fn spiral_properties(node_id: NodeId, context: &mut NodePropertiesCon ); let angle_offset = number_widget( ParameterWidgetsInfo::from_index(document_node, node_id, AngleOffsetInput::INDEX, true, context), - NumberInput::default().min(0.1).max(180.), + NumberInput::default().min(0.1).max(180.).unit(" °"), ); widgets.extend([LayoutGroup::Row { widgets: turns }, LayoutGroup::Row { widgets: angle_offset }]); 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 765bfc1eac..81b26b988a 100644 --- a/editor/src/messages/tool/common_functionality/graph_modification_utils.rs +++ b/editor/src/messages/tool/common_functionality/graph_modification_utils.rs @@ -346,6 +346,10 @@ pub fn get_star_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkIn NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name("Star") } +pub fn get_spiral_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option { + NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name("Spiral") +} + pub fn get_text_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option { NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name("Text") } diff --git a/editor/src/messages/tool/common_functionality/shapes/mod.rs b/editor/src/messages/tool/common_functionality/shapes/mod.rs index 44f40b5982..2e406db583 100644 --- a/editor/src/messages/tool/common_functionality/shapes/mod.rs +++ b/editor/src/messages/tool/common_functionality/shapes/mod.rs @@ -3,6 +3,7 @@ pub mod line_shape; pub mod polygon_shape; pub mod rectangle_shape; pub mod shape_utility; +pub mod spiral_shape; pub mod star_shape; pub use super::shapes::ellipse_shape::Ellipse; diff --git a/editor/src/messages/tool/common_functionality/shapes/polygon_shape.rs b/editor/src/messages/tool/common_functionality/shapes/polygon_shape.rs index 82dcf10cfc..c37dcf2ed8 100644 --- a/editor/src/messages/tool/common_functionality/shapes/polygon_shape.rs +++ b/editor/src/messages/tool/common_functionality/shapes/polygon_shape.rs @@ -11,9 +11,11 @@ use crate::messages::tool::common_functionality::gizmos::shape_gizmos::number_of 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::graph_modification_utils::NodeGraphLayer; 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::shape_tool::ShapeOptionsUpdate; use crate::messages::tool::tool_messages::tool_prelude::*; use glam::DAffine2; use graph_craft::document::NodeInput; @@ -148,4 +150,37 @@ impl Polygon { }); } } + + /// Updates the number of sides of a polygon or star node and syncs the Shape Tool UI widget accordingly. + /// Increases or decreases the side count based on user input, clamped to a minimum of 3. + pub fn update_sides(decrease: bool, layer: LayerNodeIdentifier, document: &DocumentMessageHandler, responses: &mut VecDeque) { + let Some(node_id) = graph_modification_utils::get_polygon_id(layer, &document.network_interface).or(graph_modification_utils::get_star_id(layer, &document.network_interface)) else { + return; + }; + + let Some(node_inputs) = NodeGraphLayer::new(layer, &document.network_interface) + .find_node_inputs("Regular Polygon") + .or(NodeGraphLayer::new(layer, &document.network_interface).find_node_inputs("Star")) + else { + return; + }; + + let Some(&TaggedValue::U32(n)) = node_inputs.get(1).unwrap().as_value() else { return }; + + responses.add(ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::Vertices(n + 1))); + + let input: NodeInput; + if decrease { + input = NodeInput::value(TaggedValue::U32((n - 1).max(3)), false); + responses.add(ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::Vertices((n - 1).max(3)))); + } else { + input = NodeInput::value(TaggedValue::U32(n + 1), false); + responses.add(ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::Vertices(n + 1))); + } + + responses.add(NodeGraphMessage::SetInput { + input_connector: InputConnector::node(node_id, 1), + input, + }); + } } 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..7bdcadf382 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, + Spiral = 2, + Rectangle = 3, + Ellipse = 4, + Line = 5, } impl ShapeType { @@ -34,6 +35,7 @@ impl ShapeType { (match self { Self::Polygon => "Polygon", Self::Star => "Star", + Self::Spiral => "Spiral", Self::Rectangle => "Rectangle", Self::Ellipse => "Ellipse", Self::Line => "Line", diff --git a/editor/src/messages/tool/common_functionality/shapes/spiral_shape.rs b/editor/src/messages/tool/common_functionality/shapes/spiral_shape.rs new file mode 100644 index 0000000000..0370c07ed8 --- /dev/null +++ b/editor/src/messages/tool/common_functionality/shapes/spiral_shape.rs @@ -0,0 +1,121 @@ +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::utility_types::document_metadata::LayerNodeIdentifier; +use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate}; +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::snapping::{SnapCandidatePoint, SnapData, SnapTypeConfiguration}; +use crate::messages::tool::tool_messages::shape_tool::ShapeOptionsUpdate; +use crate::messages::tool::tool_messages::tool_prelude::*; +use glam::DAffine2; +use graph_craft::document::NodeId; +use graph_craft::document::NodeInput; +use graph_craft::document::value::TaggedValue; +use graphene_std::vector::misc::SpiralType; +use std::collections::VecDeque; +use std::f64::consts::TAU; + +#[derive(Default)] +pub struct Spiral; + +impl Spiral { + pub fn create_node(spiral_type: SpiralType, turns: f64) -> NodeTemplate { + let node_type = resolve_document_node_type("Spiral").expect("Spiral node can't be found"); + node_type.node_template_input_override([ + None, + Some(NodeInput::value(TaggedValue::SpiralType(spiral_type), false)), + Some(NodeInput::value(TaggedValue::F64(0.001), false)), + Some(NodeInput::value(TaggedValue::F64(0.1), false)), + None, + Some(NodeInput::value(TaggedValue::F64(0.1), false)), + Some(NodeInput::value(TaggedValue::F64(turns), false)), + ]) + } + + pub fn update_shape(document: &DocumentMessageHandler, ipp: &InputPreprocessorMessageHandler, layer: LayerNodeIdentifier, shape_tool_data: &mut ShapeToolData, responses: &mut VecDeque) { + let viewport_drag_start = shape_tool_data.data.viewport_drag_start(document); + + let ignore = vec![layer]; + let snap_data = SnapData::ignore(document, ipp, &ignore); + let config = SnapTypeConfiguration::default(); + let document_mouse = document.metadata().document_to_viewport.inverse().transform_point2(ipp.mouse.position); + let snapped = shape_tool_data.data.snap_manager.free_snap(&snap_data, &SnapCandidatePoint::handle(document_mouse), config); + let snapped_viewport_point = document.metadata().document_to_viewport.transform_point2(snapped.snapped_point_document); + shape_tool_data.data.snap_manager.update_indicator(snapped); + + let dragged_distance = (viewport_drag_start - snapped_viewport_point).length(); + + let Some(node_id) = graph_modification_utils::get_spiral_id(layer, &document.network_interface) else { + return; + }; + + let Some(node_inputs) = NodeGraphLayer::new(layer, &document.network_interface).find_node_inputs("Spiral") else { + return; + }; + + let Some(&TaggedValue::F64(turns)) = node_inputs.get(6).unwrap().as_value() else { + return; + }; + + Self::update_radius(node_id, dragged_distance, turns, responses); + + responses.add(GraphOperationMessage::TransformSet { + layer, + transform: DAffine2::from_scale_angle_translation(DVec2::ONE, 0., viewport_drag_start), + transform_in: TransformIn::Viewport, + skip_rerender: false, + }); + } + + pub fn update_radius(node_id: NodeId, drag_length: f64, turns: f64, responses: &mut VecDeque) { + let archimedean_radius = drag_length / (turns * TAU); + responses.add(NodeGraphMessage::SetInput { + input_connector: InputConnector::node(node_id, 5), + input: NodeInput::value(TaggedValue::F64(archimedean_radius), false), + }); + + // 0.2 is the default parameter + let factor = (0.2 * turns * TAU).exp(); + let logarithmic_radius = drag_length / factor; + responses.add(NodeGraphMessage::SetInput { + input_connector: InputConnector::node(node_id, 2), + input: NodeInput::value(TaggedValue::F64(logarithmic_radius), false), + }); + } + + /// Updates the number of turns of a spiral node and recalculates its radius based on drag distance. + /// Also updates the Shape Tool's turns UI widget to reflect the change. + pub fn update_turns(drag_start: DVec2, decrease: bool, layer: LayerNodeIdentifier, document: &DocumentMessageHandler, ipp: &InputPreprocessorMessageHandler, responses: &mut VecDeque) { + let Some(node_id) = graph_modification_utils::get_spiral_id(layer, &document.network_interface) else { + return; + }; + + let Some(node_inputs) = NodeGraphLayer::new(layer, &document.network_interface).find_node_inputs("Spiral") else { + return; + }; + + let Some(&TaggedValue::F64(n)) = node_inputs.get(6).unwrap().as_value() else { return }; + + let input: NodeInput; + let turns: f64; + if decrease { + turns = (n - 1.).max(1.); + input = NodeInput::value(TaggedValue::F64(turns), false); + responses.add(ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::Turns(turns))); + } else { + turns = n + 1.; + input = NodeInput::value(TaggedValue::F64(turns), false); + responses.add(ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::Turns(turns))); + } + + let drag_length = drag_start.distance(ipp.mouse.position); + + Self::update_radius(node_id, drag_length, turns, responses); + + responses.add(NodeGraphMessage::SetInput { + input_connector: InputConnector::node(node_id, 6), + input, + }); + } +} diff --git a/editor/src/messages/tool/tool_messages/shape_tool.rs b/editor/src/messages/tool/tool_messages/shape_tool.rs index 5320000404..9cce9aa38f 100644 --- a/editor/src/messages/tool/tool_messages/shape_tool.rs +++ b/editor/src/messages/tool/tool_messages/shape_tool.rs @@ -3,26 +3,24 @@ use crate::consts::{DEFAULT_STROKE_WIDTH, SNAP_POINT_TOLERANCE}; use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn; 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; use crate::messages::tool::common_functionality::auto_panning::AutoPanning; use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType}; use crate::messages::tool::common_functionality::gizmos::gizmo_manager::GizmoManager; 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::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}; +use crate::messages::tool::common_functionality::shapes::spiral_shape::Spiral; use crate::messages::tool::common_functionality::shapes::star_shape::Star; use crate::messages::tool::common_functionality::shapes::{Ellipse, Line, Rectangle}; use crate::messages::tool::common_functionality::snapping::{self, SnapCandidatePoint, SnapData, SnapTypeConfiguration}; use crate::messages::tool::common_functionality::transformation_cage::{BoundingBoxManager, EdgeBool}; use crate::messages::tool::common_functionality::utility_functions::{closest_point, resize_bounds, rotate_bounds, skew_bounds, transforming_transform_cage}; -use graph_craft::document::value::TaggedValue; -use graph_craft::document::{NodeId, NodeInput}; +use graph_craft::document::NodeId; use graphene_std::Color; use graphene_std::renderer::Quad; -use graphene_std::vector::misc::ArcType; +use graphene_std::vector::misc::{ArcType, SpiralType}; use std::vec; #[derive(Default)] @@ -39,6 +37,8 @@ pub struct ShapeToolOptions { vertices: u32, shape_type: ShapeType, arc_type: ArcType, + spiral_type: SpiralType, + turns: f64, } impl Default for ShapeToolOptions { @@ -50,6 +50,8 @@ impl Default for ShapeToolOptions { vertices: 5, shape_type: ShapeType::Polygon, arc_type: ArcType::Open, + spiral_type: SpiralType::Archimedean, + turns: 5., } } } @@ -65,6 +67,8 @@ pub enum ShapeOptionsUpdate { Vertices(u32), ShapeType(ShapeType), ArcType(ArcType), + SpiralType(SpiralType), + Turns(f64), } #[impl_message(Message, ToolMessage, Shape)] @@ -101,6 +105,16 @@ fn create_sides_widget(vertices: u32) -> WidgetHolder { .widget_holder() } +fn create_turns_widget(turns: f64) -> WidgetHolder { + NumberInput::new(Some(turns)) + .label("Turns") + .min(0.5) + .max(1000.) + .mode(NumberInputMode::Increment) + .on_update(|number_input: &NumberInput| ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::Turns(number_input.value.unwrap() as f64)).into()) + .widget_holder() +} + fn create_shape_option_widget(shape_type: ShapeType) -> WidgetHolder { let entries = vec![vec![ MenuListEntry::new("Polygon") @@ -109,6 +123,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("Spiral") + .label("Spiral") + .on_commit(move |_| ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::ShapeType(ShapeType::Spiral)).into()), ]]; DropdownInput::new(entries).selected_index(Some(shape_type as u32)).widget_holder() } @@ -123,6 +140,18 @@ fn create_weight_widget(line_weight: f64) -> WidgetHolder { .widget_holder() } +fn create_spiral_type_widget(spiral_type: SpiralType) -> WidgetHolder { + let entries = vec![vec![ + MenuListEntry::new("Archimedean") + .label("Archimedean") + .on_commit(move |_| ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::SpiralType(SpiralType::Archimedean)).into()), + MenuListEntry::new("Logarithmic") + .label("Logarithmic") + .on_commit(move |_| ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::SpiralType(SpiralType::Logarithmic)).into()), + ]]; + DropdownInput::new(entries).selected_index(Some(spiral_type as u32)).widget_holder() +} + impl LayoutHolder for ShapeTool { fn layout(&self) -> Layout { let mut widgets = vec![]; @@ -137,6 +166,13 @@ impl LayoutHolder for ShapeTool { } } + if self.options.shape_type == ShapeType::Spiral { + widgets.push(create_spiral_type_widget(self.options.spiral_type)); + widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder()); + widgets.push(create_turns_widget(self.options.turns)); + 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 +239,12 @@ impl<'a> MessageHandler> for ShapeTo ShapeOptionsUpdate::ArcType(arc_type) => { self.options.arc_type = arc_type; } + ShapeOptionsUpdate::SpiralType(spiral_type) => { + self.options.spiral_type = spiral_type; + } + ShapeOptionsUpdate::Turns(turns) => { + self.options.turns = turns; + } } self.fsm_state.update_hints(responses); @@ -327,6 +369,22 @@ impl ShapeToolData { } } } + + fn increase_no_sides_turns(&self, document: &DocumentMessageHandler, ipp: &InputPreprocessorMessageHandler, shape_type: ShapeType, responses: &mut VecDeque, decrease: bool) { + if let Some(layer) = self.data.layer { + match shape_type { + ShapeType::Star | ShapeType::Polygon => { + Polygon::update_sides(decrease, layer, document, responses); + } + ShapeType::Spiral => { + Spiral::update_turns(self.data.viewport_drag_start(document), decrease, layer, document, ipp, responses); + } + _ => {} + } + } + + responses.add(NodeGraphMessage::RunDocumentGraph); + } } impl Fsm for ShapeToolFsmState { @@ -427,11 +485,24 @@ impl Fsm for ShapeToolFsmState { self } (ShapeToolFsmState::Ready(_), ShapeToolMessage::IncreaseSides) => { - responses.add(ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::Vertices(tool_options.vertices + 1))); + if matches!(tool_options.shape_type, ShapeType::Star | ShapeType::Polygon) { + responses.add(ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::Vertices(tool_options.vertices + 1))); + } + + if matches!(tool_options.shape_type, ShapeType::Spiral) { + responses.add(ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::Turns(tool_options.turns + 1.))); + } + self } (ShapeToolFsmState::Ready(_), ShapeToolMessage::DecreaseSides) => { - responses.add(ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::Vertices((tool_options.vertices - 1).max(3)))); + if matches!(tool_options.shape_type, ShapeType::Star | ShapeType::Polygon) { + responses.add(ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::Vertices((tool_options.vertices - 1).max(3)))); + } + + if matches!(tool_options.shape_type, ShapeType::Spiral) { + responses.add(ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::Turns((tool_options.turns - 1.).max(1.)))); + } self } ( @@ -468,61 +539,11 @@ impl Fsm for ShapeToolFsmState { self } (ShapeToolFsmState::Drawing(_), ShapeToolMessage::IncreaseSides) => { - if let Some(layer) = tool_data.data.layer { - let Some(node_id) = graph_modification_utils::get_polygon_id(layer, &document.network_interface).or(graph_modification_utils::get_star_id(layer, &document.network_interface)) - else { - return self; - }; - - let Some(node_inputs) = NodeGraphLayer::new(layer, &document.network_interface) - .find_node_inputs("Regular Polygon") - .or(NodeGraphLayer::new(layer, &document.network_interface).find_node_inputs("Star")) - else { - return self; - }; - - let Some(&TaggedValue::U32(n)) = node_inputs.get(1).unwrap().as_value() else { - return self; - }; - - responses.add(ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::Vertices(n + 1))); - - responses.add(NodeGraphMessage::SetInput { - input_connector: InputConnector::node(node_id, 1), - input: NodeInput::value(TaggedValue::U32(n + 1), false), - }); - responses.add(NodeGraphMessage::RunDocumentGraph); - } - + tool_data.increase_no_sides_turns(document, input, tool_options.shape_type, responses, false); self } (ShapeToolFsmState::Drawing(_), ShapeToolMessage::DecreaseSides) => { - if let Some(layer) = tool_data.data.layer { - let Some(node_id) = graph_modification_utils::get_polygon_id(layer, &document.network_interface).or(graph_modification_utils::get_star_id(layer, &document.network_interface)) - else { - return self; - }; - - let Some(node_inputs) = NodeGraphLayer::new(layer, &document.network_interface) - .find_node_inputs("Regular Polygon") - .or(NodeGraphLayer::new(layer, &document.network_interface).find_node_inputs("Star")) - else { - return self; - }; - - let Some(&TaggedValue::U32(n)) = node_inputs.get(1).unwrap().as_value() else { - return self; - }; - - responses.add(ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::Vertices((n - 1).max(3)))); - - responses.add(NodeGraphMessage::SetInput { - input_connector: InputConnector::node(node_id, 1), - input: NodeInput::value(TaggedValue::U32((n - 1).max(3)), false), - }); - responses.add(NodeGraphMessage::RunDocumentGraph); - } - + tool_data.increase_no_sides_turns(document, input, tool_options.shape_type, responses, true); self } (ShapeToolFsmState::Ready(_), ShapeToolMessage::DragStart) => { @@ -578,7 +599,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::Spiral => 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 +615,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::Spiral => Spiral::create_node(tool_options.spiral_type, tool_options.turns), }; let nodes = vec![(NodeId(0), node)]; @@ -602,7 +624,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::Spiral => { responses.add(GraphOperationMessage::TransformSet { layer, transform: DAffine2::from_scale_angle_translation(DVec2::ONE, 0., input.mouse.position), @@ -635,6 +657,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::Spiral => Spiral::update_shape(document, input, layer, tool_data, responses), } // Auto-panning @@ -813,6 +836,7 @@ impl Fsm for ShapeToolFsmState { responses.add(DocumentMessage::AbortTransaction); tool_data.data.cleanup(responses); tool_data.current_shape = shape; + responses.add(ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::ShapeType(shape))); ShapeToolFsmState::Ready(shape) } @@ -837,6 +861,10 @@ impl Fsm for ShapeToolFsmState { ]), HintGroup(vec![HintInfo::multi_keys([[Key::BracketLeft], [Key::BracketRight]], "Decrease/Increase Sides")]), ], + ShapeType::Spiral => vec![ + HintGroup(vec![HintInfo::mouse(MouseMotion::LmbDrag, "Draw Spiral")]), + HintGroup(vec![HintInfo::multi_keys([[Key::BracketLeft], [Key::BracketRight]], "Decrease/Increase Turns")]), + ], ShapeType::Ellipse => vec![HintGroup(vec![ HintInfo::mouse(MouseMotion::LmbDrag, "Draw Ellipse"), HintInfo::keys([Key::Shift], "Constrain Circular").prepend_plus(), @@ -867,6 +895,7 @@ impl Fsm for ShapeToolFsmState { HintInfo::keys([Key::Alt], "From Center"), HintInfo::keys([Key::Control], "Lock Angle"), ]), + _ => HintGroup(vec![]), }; common_hint_group.push(tool_hint_group); @@ -875,6 +904,10 @@ impl Fsm for ShapeToolFsmState { common_hint_group.push(HintGroup(vec![HintInfo::multi_keys([[Key::BracketLeft], [Key::BracketRight]], "Decrease/Increase Sides")])); } + if matches!(shape, ShapeType::Spiral) { + common_hint_group.push(HintGroup(vec![HintInfo::multi_keys([[Key::BracketLeft], [Key::BracketRight]], "Decrease/Increase Turns")])); + } + HintData(common_hint_group) } ShapeToolFsmState::DraggingLineEndpoints => HintData(vec![ diff --git a/node-graph/gcore/src/vector/generator_nodes.rs b/node-graph/gcore/src/vector/generator_nodes.rs index 63a338186f..4774be4e10 100644 --- a/node-graph/gcore/src/vector/generator_nodes.rs +++ b/node-graph/gcore/src/vector/generator_nodes.rs @@ -80,7 +80,7 @@ fn spiral( _primary: (), spiral_type: SpiralType, #[default(0.5)] start_radius: f64, - #[default(1.)] inner_radius: f64, + #[default(0.)] inner_radius: f64, #[default(0.2)] growth: f64, #[default(1.)] tightness: f64, #[default(6)] turns: f64,