integrated grid shape in shape-tool

This commit is contained in:
0SlowPoke0
2025-07-19 00:52:53 +05:30
parent 561b671f8d
commit 3bc206843a
8 changed files with 232 additions and 10 deletions

View File

@@ -52,5 +52,6 @@
"files.insertFinalNewline": true,
"files.associations": {
"*.graphite": "json"
}
},
"rust-analyzer.checkOnSave": false
}

View File

@@ -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<NodeId> {
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")?;

View File

@@ -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<Message>) {
// 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<Message>) {
// 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<LayerNodeIdentifier>,
// 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<Message>,
) {
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,
});
}
}

View File

@@ -1,4 +1,5 @@
pub mod ellipse_shape;
pub mod grid_shape;
pub mod line_shape;
pub mod polygon_shape;
pub mod rectangle_shape;

View File

@@ -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()
}

View File

@@ -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<ToolMessage, &mut ToolActionMessageContext<'a>> 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"),

View File

@@ -56,8 +56,8 @@ impl AsI64 for f64 {
#[widget(Radio)]
pub enum GridType {
#[default]
Rectangular,
Isometric,
Rectangular = 0,
Isometric = 1,
}
#[repr(C)]

View File

@@ -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);
}