mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-12-23 10:11:54 +00:00
Add Heart shape tool
- Add new_heart() method - Add Heart node to vector generator nodes - Add Heart to ShapeType enum and UI dropdown
This commit is contained in:
parent
5ebf6d6bc0
commit
a78ee3c52a
7 changed files with 106 additions and 7 deletions
|
|
@ -376,6 +376,10 @@ pub fn get_grid_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkIn
|
|||
NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name("Grid")
|
||||
}
|
||||
|
||||
pub fn get_heart_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<NodeId> {
|
||||
NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name("Heart")
|
||||
}
|
||||
|
||||
/// 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")?;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
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::shapes::shape_utility::ShapeToolModifierKey;
|
||||
use crate::messages::tool::tool_messages::shape_tool::ShapeToolData;
|
||||
use crate::messages::tool::tool_messages::tool_prelude::*;
|
||||
use glam::DAffine2;
|
||||
use graph_craft::document::NodeInput;
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Heart;
|
||||
|
||||
impl Heart {
|
||||
pub fn create_node() -> NodeTemplate {
|
||||
let node_type = resolve_document_node_type("Heart").expect("Heart node can't be found");
|
||||
node_type.node_template_input_override([None, Some(NodeInput::value(TaggedValue::F64(0.), false))])
|
||||
}
|
||||
|
||||
pub fn update_shape(
|
||||
document: &DocumentMessageHandler,
|
||||
ipp: &InputPreprocessorMessageHandler,
|
||||
viewport: &ViewportMessageHandler,
|
||||
layer: LayerNodeIdentifier,
|
||||
shape_tool_data: &mut ShapeToolData,
|
||||
modifier: ShapeToolModifierKey,
|
||||
responses: &mut VecDeque<Message>,
|
||||
) {
|
||||
let center = modifier[0];
|
||||
let [start, end] = shape_tool_data.data.calculate_circle_points(document, ipp, viewport, center);
|
||||
let Some(node_id) = graph_modification_utils::get_heart_id(layer, &document.network_interface) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let dimensions = (start - end).abs();
|
||||
|
||||
let radius: f64 = if dimensions.x > dimensions.y { dimensions.y / 2. } else { dimensions.x / 2. };
|
||||
|
||||
responses.add(NodeGraphMessage::SetInput {
|
||||
input_connector: InputConnector::node(node_id, 1),
|
||||
input: NodeInput::value(TaggedValue::F64(radius), false),
|
||||
});
|
||||
|
||||
responses.add(GraphOperationMessage::TransformSet {
|
||||
layer,
|
||||
transform: DAffine2::from_scale_angle_translation(DVec2::ONE, 0., start.midpoint(end)),
|
||||
transform_in: TransformIn::Viewport,
|
||||
skip_rerender: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ pub mod arc_shape;
|
|||
pub mod circle_shape;
|
||||
pub mod ellipse_shape;
|
||||
pub mod grid_shape;
|
||||
pub mod heart_shape;
|
||||
pub mod line_shape;
|
||||
pub mod polygon_shape;
|
||||
pub mod rectangle_shape;
|
||||
|
|
@ -10,6 +11,7 @@ pub mod spiral_shape;
|
|||
pub mod star_shape;
|
||||
|
||||
pub use super::shapes::ellipse_shape::Ellipse;
|
||||
pub use super::shapes::heart_shape::Heart;
|
||||
pub use super::shapes::line_shape::{Line, LineEnd};
|
||||
pub use super::shapes::rectangle_shape::Rectangle;
|
||||
pub use crate::messages::tool::tool_messages::shape_tool::ShapeToolData;
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ pub enum ShapeType {
|
|||
Arc,
|
||||
Spiral,
|
||||
Grid,
|
||||
Heart,
|
||||
Rectangle,
|
||||
Ellipse,
|
||||
Line,
|
||||
|
|
@ -45,6 +46,7 @@ impl ShapeType {
|
|||
Self::Arc => "Arc",
|
||||
Self::Grid => "Grid",
|
||||
Self::Spiral => "Spiral",
|
||||
Self::Heart => "Heart",
|
||||
Self::Rectangle => "Rectangle",
|
||||
Self::Ellipse => "Ellipse",
|
||||
Self::Line => "Line",
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ use crate::messages::tool::common_functionality::resize::Resize;
|
|||
use crate::messages::tool::common_functionality::shapes::arc_shape::Arc;
|
||||
use crate::messages::tool::common_functionality::shapes::circle_shape::Circle;
|
||||
use crate::messages::tool::common_functionality::shapes::grid_shape::Grid;
|
||||
use crate::messages::tool::common_functionality::shapes::heart_shape::Heart;
|
||||
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};
|
||||
|
|
@ -168,6 +169,12 @@ fn create_shape_option_widget(shape_type: ShapeType) -> WidgetHolder {
|
|||
}
|
||||
.into()
|
||||
}),
|
||||
MenuListEntry::new("Heart").label("Heart").on_commit(move |_| {
|
||||
ShapeToolMessage::UpdateOptions {
|
||||
options: ShapeOptionsUpdate::ShapeType(ShapeType::Heart),
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
]];
|
||||
DropdownInput::new(entries).selected_index(Some(shape_type as u32)).widget_holder()
|
||||
}
|
||||
|
|
@ -806,7 +813,7 @@ impl Fsm for ShapeToolFsmState {
|
|||
};
|
||||
|
||||
match tool_data.current_shape {
|
||||
ShapeType::Polygon | ShapeType::Star | ShapeType::Circle | ShapeType::Arc | ShapeType::Spiral | ShapeType::Grid | ShapeType::Rectangle | ShapeType::Ellipse => {
|
||||
ShapeType::Polygon | ShapeType::Star | ShapeType::Circle | ShapeType::Arc | ShapeType::Spiral | ShapeType::Grid | ShapeType::Heart | ShapeType::Rectangle | ShapeType::Ellipse => {
|
||||
tool_data.data.start(document, input, viewport);
|
||||
}
|
||||
ShapeType::Line => {
|
||||
|
|
@ -828,6 +835,7 @@ impl Fsm for ShapeToolFsmState {
|
|||
ShapeType::Arc => Arc::create_node(tool_options.arc_type),
|
||||
ShapeType::Spiral => Spiral::create_node(tool_options.spiral_type, tool_options.turns),
|
||||
ShapeType::Grid => Grid::create_node(tool_options.grid_type),
|
||||
ShapeType::Heart => Heart::create_node(),
|
||||
ShapeType::Rectangle => Rectangle::create_node(),
|
||||
ShapeType::Ellipse => Ellipse::create_node(),
|
||||
ShapeType::Line => Line::create_node(document, tool_data.data.drag_start),
|
||||
|
|
@ -839,7 +847,7 @@ impl Fsm for ShapeToolFsmState {
|
|||
let defered_responses = &mut VecDeque::new();
|
||||
|
||||
match tool_data.current_shape {
|
||||
ShapeType::Polygon | ShapeType::Star | ShapeType::Circle | ShapeType::Arc | ShapeType::Spiral | ShapeType::Grid | ShapeType::Rectangle | ShapeType::Ellipse => {
|
||||
ShapeType::Polygon | ShapeType::Star | ShapeType::Circle | ShapeType::Arc | ShapeType::Spiral | ShapeType::Grid | ShapeType::Heart | ShapeType::Rectangle | ShapeType::Ellipse => {
|
||||
defered_responses.add(GraphOperationMessage::TransformSet {
|
||||
layer,
|
||||
transform: DAffine2::from_scale_angle_translation(DVec2::ONE, 0., input.mouse.position),
|
||||
|
|
@ -878,6 +886,7 @@ impl Fsm for ShapeToolFsmState {
|
|||
ShapeType::Arc => Arc::update_shape(document, input, viewport, layer, tool_data, modifier, responses),
|
||||
ShapeType::Spiral => Spiral::update_shape(document, input, viewport, layer, tool_data, responses),
|
||||
ShapeType::Grid => Grid::update_shape(document, input, layer, tool_options.grid_type, tool_data, modifier, responses),
|
||||
ShapeType::Heart => Heart::update_shape(document, input, viewport, layer, tool_data, modifier, responses),
|
||||
ShapeType::Rectangle => Rectangle::update_shape(document, input, viewport, layer, tool_data, modifier, responses),
|
||||
ShapeType::Ellipse => Ellipse::update_shape(document, input, viewport, layer, tool_data, modifier, responses),
|
||||
ShapeType::Line => Line::update_shape(document, input, viewport, layer, tool_data, modifier, responses),
|
||||
|
|
@ -1118,10 +1127,14 @@ fn update_dynamic_hints(state: &ShapeToolFsmState, responses: &mut VecDeque<Mess
|
|||
HintInfo::keys([Key::Shift], "Constrain Square").prepend_plus(),
|
||||
HintInfo::keys([Key::Alt], "From Center").prepend_plus(),
|
||||
])],
|
||||
ShapeType::Circle => vec![HintGroup(vec![
|
||||
HintInfo::mouse(MouseMotion::LmbDrag, "Draw Circle"),
|
||||
HintInfo::keys([Key::Alt], "From Center").prepend_plus(),
|
||||
])],
|
||||
ShapeType::Circle => vec![HintGroup(vec![
|
||||
HintInfo::mouse(MouseMotion::LmbDrag, "Draw Circle"),
|
||||
HintInfo::keys([Key::Alt], "From Center").prepend_plus(),
|
||||
])],
|
||||
ShapeType::Heart => vec![HintGroup(vec![
|
||||
HintInfo::mouse(MouseMotion::LmbDrag, "Draw Heart"),
|
||||
HintInfo::keys([Key::Alt], "From Center").prepend_plus(),
|
||||
])],
|
||||
ShapeType::Arc => vec![HintGroup(vec![
|
||||
HintInfo::mouse(MouseMotion::LmbDrag, "Draw Arc"),
|
||||
HintInfo::keys([Key::Shift], "Constrain Arc").prepend_plus(),
|
||||
|
|
@ -1147,7 +1160,7 @@ fn update_dynamic_hints(state: &ShapeToolFsmState, responses: &mut VecDeque<Mess
|
|||
HintInfo::keys([Key::Alt], "From Center"),
|
||||
HintInfo::keys([Key::Control], "Lock Angle"),
|
||||
]),
|
||||
ShapeType::Circle => HintGroup(vec![HintInfo::keys([Key::Alt], "From Center")]),
|
||||
ShapeType::Circle | ShapeType::Heart => HintGroup(vec![HintInfo::keys([Key::Alt], "From Center")]),
|
||||
ShapeType::Spiral => HintGroup(vec![]),
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -317,6 +317,18 @@ impl<PointId: Identifier> Subpath<PointId> {
|
|||
Self::from_anchors([p1, p2], false)
|
||||
}
|
||||
|
||||
/// Constructs a heart shape centered at the origin with the given radius.
|
||||
pub fn new_heart(center: DVec2, radius: f64) -> Self {
|
||||
let bottom = center + DVec2::new(0., radius);
|
||||
let top = center + DVec2::new(0., -radius * 0.4);
|
||||
|
||||
let manipulator_groups = vec![
|
||||
ManipulatorGroup::new(bottom, Some(bottom + DVec2::new(radius * 1.2, -radius * 0.9)), Some(bottom + DVec2::new(-radius * 1.2, -radius * 0.9))),
|
||||
ManipulatorGroup::new(top, Some(top + DVec2::new(-radius * 1.2, -radius * 0.6)), Some(top + DVec2::new(radius * 1.2, -radius * 0.6))),
|
||||
];
|
||||
Self::new(manipulator_groups, true)
|
||||
}
|
||||
|
||||
pub fn new_spiral(a: f64, outer_radius: f64, turns: f64, start_angle: f64, delta_theta: f64, spiral_type: SpiralType) -> Self {
|
||||
let mut manipulator_groups = Vec::new();
|
||||
let mut prev_in_handle = None;
|
||||
|
|
|
|||
|
|
@ -201,6 +201,19 @@ fn line(
|
|||
Table::new_from_element(Vector::from_subpath(subpath::Subpath::new_line(start, end)))
|
||||
}
|
||||
|
||||
/// Generates a heart shape with a chosen radius.
|
||||
#[node_macro::node(category("Vector: Shape"))]
|
||||
fn heart(
|
||||
_: impl Ctx,
|
||||
_primary: (),
|
||||
#[unit(" px")]
|
||||
#[default(50.)]
|
||||
radius: f64,
|
||||
) -> Table<Vector> {
|
||||
let radius = radius.abs();
|
||||
Table::new_from_element(Vector::from_subpath(subpath::Subpath::new_heart(DVec2::ZERO, radius)))
|
||||
}
|
||||
|
||||
trait GridSpacing {
|
||||
fn as_dvec2(&self) -> DVec2;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue