mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-26 05:18:13 +08:00
add spiral shape in shape tool
This commit is contained in:
@@ -1251,11 +1251,17 @@ pub(crate) fn spiral_properties(node_id: NodeId, context: &mut NodePropertiesCon
|
|||||||
match spiral_type {
|
match spiral_type {
|
||||||
SpiralType::Archimedean => {
|
SpiralType::Archimedean => {
|
||||||
let start_radius = LayoutGroup::Row {
|
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 {
|
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]);
|
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 {
|
let start_radius = LayoutGroup::Row {
|
||||||
widgets: number_widget(
|
widgets: number_widget(
|
||||||
ParameterWidgetsInfo::from_index(document_node, node_id, StartRadiusInput::INDEX, true, context),
|
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 {
|
let growth = LayoutGroup::Row {
|
||||||
widgets: number_widget(
|
widgets: number_widget(
|
||||||
ParameterWidgetsInfo::from_index(document_node, node_id, GrowthInput::INDEX, true, context),
|
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(
|
let angle_offset = number_widget(
|
||||||
ParameterWidgetsInfo::from_index(document_node, node_id, AngleOffsetInput::INDEX, true, context),
|
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 }]);
|
widgets.extend([LayoutGroup::Row { widgets: turns }, LayoutGroup::Row { widgets: angle_offset }]);
|
||||||
|
|||||||
@@ -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")
|
NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name("Star")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_spiral_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<NodeId> {
|
||||||
|
NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name("Spiral")
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_text_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<NodeId> {
|
pub fn get_text_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<NodeId> {
|
||||||
NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name("Text")
|
NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name("Text")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ pub mod line_shape;
|
|||||||
pub mod polygon_shape;
|
pub mod polygon_shape;
|
||||||
pub mod rectangle_shape;
|
pub mod rectangle_shape;
|
||||||
pub mod shape_utility;
|
pub mod shape_utility;
|
||||||
|
pub mod spiral_shape;
|
||||||
pub mod star_shape;
|
pub mod star_shape;
|
||||||
|
|
||||||
pub use super::shapes::ellipse_shape::Ellipse;
|
pub use super::shapes::ellipse_shape::Ellipse;
|
||||||
|
|||||||
@@ -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::PointRadiusHandle;
|
||||||
use crate::messages::tool::common_functionality::gizmos::shape_gizmos::point_radius_handle::PointRadiusHandleState;
|
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;
|
||||||
|
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::shape_editor::ShapeState;
|
||||||
use crate::messages::tool::common_functionality::shapes::shape_utility::ShapeGizmoHandler;
|
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::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 crate::messages::tool::tool_messages::tool_prelude::*;
|
||||||
use glam::DAffine2;
|
use glam::DAffine2;
|
||||||
use graph_craft::document::NodeInput;
|
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<Message>) {
|
||||||
|
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,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,9 +24,10 @@ pub enum ShapeType {
|
|||||||
#[default]
|
#[default]
|
||||||
Polygon = 0,
|
Polygon = 0,
|
||||||
Star = 1,
|
Star = 1,
|
||||||
Rectangle = 2,
|
Spiral = 2,
|
||||||
Ellipse = 3,
|
Rectangle = 3,
|
||||||
Line = 4,
|
Ellipse = 4,
|
||||||
|
Line = 5,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ShapeType {
|
impl ShapeType {
|
||||||
@@ -34,6 +35,7 @@ impl ShapeType {
|
|||||||
(match self {
|
(match self {
|
||||||
Self::Polygon => "Polygon",
|
Self::Polygon => "Polygon",
|
||||||
Self::Star => "Star",
|
Self::Star => "Star",
|
||||||
|
Self::Spiral => "Spiral",
|
||||||
Self::Rectangle => "Rectangle",
|
Self::Rectangle => "Rectangle",
|
||||||
Self::Ellipse => "Ellipse",
|
Self::Ellipse => "Ellipse",
|
||||||
Self::Line => "Line",
|
Self::Line => "Line",
|
||||||
|
|||||||
@@ -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<Message>) {
|
||||||
|
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<Message>) {
|
||||||
|
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<Message>) {
|
||||||
|
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,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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::graph_operation::utility_types::TransformIn;
|
||||||
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
|
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::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::auto_panning::AutoPanning;
|
||||||
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType};
|
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::gizmos::gizmo_manager::GizmoManager;
|
||||||
use crate::messages::tool::common_functionality::graph_modification_utils;
|
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::resize::Resize;
|
||||||
use crate::messages::tool::common_functionality::shapes::line_shape::{LineToolData, clicked_on_line_endpoints};
|
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::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::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::star_shape::Star;
|
||||||
use crate::messages::tool::common_functionality::shapes::{Ellipse, Line, Rectangle};
|
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::snapping::{self, SnapCandidatePoint, SnapData, SnapTypeConfiguration};
|
||||||
use crate::messages::tool::common_functionality::transformation_cage::{BoundingBoxManager, EdgeBool};
|
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 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;
|
||||||
use graph_craft::document::{NodeId, NodeInput};
|
|
||||||
use graphene_std::Color;
|
use graphene_std::Color;
|
||||||
use graphene_std::renderer::Quad;
|
use graphene_std::renderer::Quad;
|
||||||
use graphene_std::vector::misc::ArcType;
|
use graphene_std::vector::misc::{ArcType, SpiralType};
|
||||||
use std::vec;
|
use std::vec;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@@ -39,6 +37,8 @@ pub struct ShapeToolOptions {
|
|||||||
vertices: u32,
|
vertices: u32,
|
||||||
shape_type: ShapeType,
|
shape_type: ShapeType,
|
||||||
arc_type: ArcType,
|
arc_type: ArcType,
|
||||||
|
spiral_type: SpiralType,
|
||||||
|
turns: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ShapeToolOptions {
|
impl Default for ShapeToolOptions {
|
||||||
@@ -50,6 +50,8 @@ impl Default for ShapeToolOptions {
|
|||||||
vertices: 5,
|
vertices: 5,
|
||||||
shape_type: ShapeType::Polygon,
|
shape_type: ShapeType::Polygon,
|
||||||
arc_type: ArcType::Open,
|
arc_type: ArcType::Open,
|
||||||
|
spiral_type: SpiralType::Archimedean,
|
||||||
|
turns: 5.,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -65,6 +67,8 @@ pub enum ShapeOptionsUpdate {
|
|||||||
Vertices(u32),
|
Vertices(u32),
|
||||||
ShapeType(ShapeType),
|
ShapeType(ShapeType),
|
||||||
ArcType(ArcType),
|
ArcType(ArcType),
|
||||||
|
SpiralType(SpiralType),
|
||||||
|
Turns(f64),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[impl_message(Message, ToolMessage, Shape)]
|
#[impl_message(Message, ToolMessage, Shape)]
|
||||||
@@ -101,6 +105,16 @@ fn create_sides_widget(vertices: u32) -> WidgetHolder {
|
|||||||
.widget_holder()
|
.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 {
|
fn create_shape_option_widget(shape_type: ShapeType) -> WidgetHolder {
|
||||||
let entries = vec![vec![
|
let entries = vec![vec![
|
||||||
MenuListEntry::new("Polygon")
|
MenuListEntry::new("Polygon")
|
||||||
@@ -109,6 +123,9 @@ fn create_shape_option_widget(shape_type: ShapeType) -> WidgetHolder {
|
|||||||
MenuListEntry::new("Star")
|
MenuListEntry::new("Star")
|
||||||
.label("Star")
|
.label("Star")
|
||||||
.on_commit(move |_| ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::ShapeType(ShapeType::Star)).into()),
|
.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()
|
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()
|
.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 {
|
impl LayoutHolder for ShapeTool {
|
||||||
fn layout(&self) -> Layout {
|
fn layout(&self) -> Layout {
|
||||||
let mut widgets = vec![];
|
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 {
|
if self.options.shape_type != ShapeType::Line {
|
||||||
widgets.append(&mut self.options.fill.create_widgets(
|
widgets.append(&mut self.options.fill.create_widgets(
|
||||||
"Fill",
|
"Fill",
|
||||||
@@ -203,6 +239,12 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for ShapeTo
|
|||||||
ShapeOptionsUpdate::ArcType(arc_type) => {
|
ShapeOptionsUpdate::ArcType(arc_type) => {
|
||||||
self.options.arc_type = 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);
|
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<Message>, 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 {
|
impl Fsm for ShapeToolFsmState {
|
||||||
@@ -427,11 +485,24 @@ impl Fsm for ShapeToolFsmState {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
(ShapeToolFsmState::Ready(_), ShapeToolMessage::IncreaseSides) => {
|
(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
|
self
|
||||||
}
|
}
|
||||||
(ShapeToolFsmState::Ready(_), ShapeToolMessage::DecreaseSides) => {
|
(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
|
self
|
||||||
}
|
}
|
||||||
(
|
(
|
||||||
@@ -468,61 +539,11 @@ impl Fsm for ShapeToolFsmState {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
(ShapeToolFsmState::Drawing(_), ShapeToolMessage::IncreaseSides) => {
|
(ShapeToolFsmState::Drawing(_), ShapeToolMessage::IncreaseSides) => {
|
||||||
if let Some(layer) = tool_data.data.layer {
|
tool_data.increase_no_sides_turns(document, input, tool_options.shape_type, responses, false);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
(ShapeToolFsmState::Drawing(_), ShapeToolMessage::DecreaseSides) => {
|
(ShapeToolFsmState::Drawing(_), ShapeToolMessage::DecreaseSides) => {
|
||||||
if let Some(layer) = tool_data.data.layer {
|
tool_data.increase_no_sides_turns(document, input, tool_options.shape_type, responses, true);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
(ShapeToolFsmState::Ready(_), ShapeToolMessage::DragStart) => {
|
(ShapeToolFsmState::Ready(_), ShapeToolMessage::DragStart) => {
|
||||||
@@ -578,7 +599,7 @@ impl Fsm for ShapeToolFsmState {
|
|||||||
};
|
};
|
||||||
|
|
||||||
match tool_data.current_shape {
|
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 => {
|
ShapeType::Line => {
|
||||||
let point = SnapCandidatePoint::handle(document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position));
|
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());
|
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::Rectangle => Rectangle::create_node(),
|
||||||
ShapeType::Ellipse => Ellipse::create_node(),
|
ShapeType::Ellipse => Ellipse::create_node(),
|
||||||
ShapeType::Line => Line::create_node(document, tool_data.data.drag_start),
|
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)];
|
let nodes = vec![(NodeId(0), node)];
|
||||||
@@ -602,7 +624,7 @@ impl Fsm for ShapeToolFsmState {
|
|||||||
responses.add(Message::StartBuffer);
|
responses.add(Message::StartBuffer);
|
||||||
|
|
||||||
match tool_data.current_shape {
|
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 {
|
responses.add(GraphOperationMessage::TransformSet {
|
||||||
layer,
|
layer,
|
||||||
transform: DAffine2::from_scale_angle_translation(DVec2::ONE, 0., input.mouse.position),
|
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::Line => Line::update_shape(document, input, layer, tool_data, modifier, responses),
|
||||||
ShapeType::Polygon => Polygon::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::Star => Star::update_shape(document, input, layer, tool_data, modifier, responses),
|
||||||
|
ShapeType::Spiral => Spiral::update_shape(document, input, layer, tool_data, responses),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auto-panning
|
// Auto-panning
|
||||||
@@ -813,6 +836,7 @@ impl Fsm for ShapeToolFsmState {
|
|||||||
responses.add(DocumentMessage::AbortTransaction);
|
responses.add(DocumentMessage::AbortTransaction);
|
||||||
tool_data.data.cleanup(responses);
|
tool_data.data.cleanup(responses);
|
||||||
tool_data.current_shape = shape;
|
tool_data.current_shape = shape;
|
||||||
|
responses.add(ShapeToolMessage::UpdateOptions(ShapeOptionsUpdate::ShapeType(shape)));
|
||||||
|
|
||||||
ShapeToolFsmState::Ready(shape)
|
ShapeToolFsmState::Ready(shape)
|
||||||
}
|
}
|
||||||
@@ -837,6 +861,10 @@ impl Fsm for ShapeToolFsmState {
|
|||||||
]),
|
]),
|
||||||
HintGroup(vec![HintInfo::multi_keys([[Key::BracketLeft], [Key::BracketRight]], "Decrease/Increase Sides")]),
|
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![
|
ShapeType::Ellipse => vec![HintGroup(vec![
|
||||||
HintInfo::mouse(MouseMotion::LmbDrag, "Draw Ellipse"),
|
HintInfo::mouse(MouseMotion::LmbDrag, "Draw Ellipse"),
|
||||||
HintInfo::keys([Key::Shift], "Constrain Circular").prepend_plus(),
|
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::Alt], "From Center"),
|
||||||
HintInfo::keys([Key::Control], "Lock Angle"),
|
HintInfo::keys([Key::Control], "Lock Angle"),
|
||||||
]),
|
]),
|
||||||
|
_ => HintGroup(vec![]),
|
||||||
};
|
};
|
||||||
|
|
||||||
common_hint_group.push(tool_hint_group);
|
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")]));
|
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)
|
HintData(common_hint_group)
|
||||||
}
|
}
|
||||||
ShapeToolFsmState::DraggingLineEndpoints => HintData(vec![
|
ShapeToolFsmState::DraggingLineEndpoints => HintData(vec![
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ fn spiral(
|
|||||||
_primary: (),
|
_primary: (),
|
||||||
spiral_type: SpiralType,
|
spiral_type: SpiralType,
|
||||||
#[default(0.5)] start_radius: f64,
|
#[default(0.5)] start_radius: f64,
|
||||||
#[default(1.)] inner_radius: f64,
|
#[default(0.)] inner_radius: f64,
|
||||||
#[default(0.2)] growth: f64,
|
#[default(0.2)] growth: f64,
|
||||||
#[default(1.)] tightness: f64,
|
#[default(1.)] tightness: f64,
|
||||||
#[default(6)] turns: f64,
|
#[default(6)] turns: f64,
|
||||||
|
|||||||
Reference in New Issue
Block a user