moved line gizmos into gizmo manager

This commit is contained in:
0SlowPoke0
2025-08-22 01:13:47 +05:30
parent 2c8913416d
commit 2790ea99fd
13 changed files with 382 additions and 251 deletions

View File

@@ -7,9 +7,11 @@ 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::arc_shape::ArcGizmoHandler;
use crate::messages::tool::common_functionality::shapes::circle_shape::CircleGizmoHandler;
use crate::messages::tool::common_functionality::shapes::line_shape::LineGizmoHandler;
use crate::messages::tool::common_functionality::shapes::polygon_shape::PolygonGizmoHandler;
use crate::messages::tool::common_functionality::shapes::shape_utility::ShapeGizmoHandler;
use crate::messages::tool::common_functionality::shapes::star_shape::StarGizmoHandler;
use crate::messages::tool::common_functionality::snapping::SnapManager;
use glam::DVec2;
use std::collections::VecDeque;
@@ -24,6 +26,7 @@ use std::collections::VecDeque;
pub enum ShapeGizmoHandlers {
#[default]
None,
Line(LineGizmoHandler),
Star(StarGizmoHandler),
Polygon(PolygonGizmoHandler),
Arc(ArcGizmoHandler),
@@ -35,6 +38,7 @@ impl ShapeGizmoHandlers {
/// Used for grouping logic and distinguishing between handler types at runtime.
pub fn kind(&self) -> &'static str {
match self {
Self::Line(_) => "Line",
Self::Star(_) => "star",
Self::Polygon(_) => "polygon",
Self::Arc(_) => "arc",
@@ -44,12 +48,13 @@ impl ShapeGizmoHandlers {
}
/// Dispatches interaction state updates to the corresponding shape-specific handler.
pub fn handle_state(&mut self, layer: LayerNodeIdentifier, mouse_position: DVec2, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
pub fn handle_state(&mut self, layer: LayerNodeIdentifier, mouse_position: DVec2, document: &DocumentMessageHandler) {
match self {
Self::Star(h) => h.handle_state(layer, mouse_position, document, responses),
Self::Polygon(h) => h.handle_state(layer, mouse_position, document, responses),
Self::Arc(h) => h.handle_state(layer, mouse_position, document, responses),
Self::Circle(h) => h.handle_state(layer, mouse_position, document, responses),
Self::Line(h) => h.handle_state(layer, mouse_position, document),
Self::Star(h) => h.handle_state(layer, mouse_position, document),
Self::Polygon(h) => h.handle_state(layer, mouse_position, document),
Self::Arc(h) => h.handle_state(layer, mouse_position, document),
Self::Circle(h) => h.handle_state(layer, mouse_position, document),
Self::None => {}
}
}
@@ -57,6 +62,7 @@ impl ShapeGizmoHandlers {
/// Checks if any interactive part of the gizmo is currently hovered.
pub fn is_any_gizmo_hovered(&self) -> bool {
match self {
Self::Line(h) => h.is_any_gizmo_hovered(),
Self::Star(h) => h.is_any_gizmo_hovered(),
Self::Polygon(h) => h.is_any_gizmo_hovered(),
Self::Arc(h) => h.is_any_gizmo_hovered(),
@@ -68,6 +74,7 @@ impl ShapeGizmoHandlers {
/// Passes the click interaction to the appropriate gizmo handler if one is hovered.
pub fn handle_click(&mut self) {
match self {
Self::Line(h) => h.handle_click(),
Self::Star(h) => h.handle_click(),
Self::Polygon(h) => h.handle_click(),
Self::Arc(h) => h.handle_click(),
@@ -77,12 +84,13 @@ impl ShapeGizmoHandlers {
}
/// Updates the gizmo state while the user is dragging a handle (e.g., adjusting radius).
pub fn handle_update(&mut self, drag_start: DVec2, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) {
pub fn handle_update(&mut self, drag_start: DVec2, snap_manager: &mut SnapManager, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) {
match self {
Self::Star(h) => h.handle_update(drag_start, document, input, responses),
Self::Polygon(h) => h.handle_update(drag_start, document, input, responses),
Self::Arc(h) => h.handle_update(drag_start, document, input, responses),
Self::Circle(h) => h.handle_update(drag_start, document, input, responses),
Self::Line(h) => h.handle_update(drag_start, snap_manager, document, input, responses),
Self::Star(h) => h.handle_update(drag_start, snap_manager, document, input, responses),
Self::Polygon(h) => h.handle_update(drag_start, snap_manager, document, input, responses),
Self::Arc(h) => h.handle_update(drag_start, snap_manager, document, input, responses),
Self::Circle(h) => h.handle_update(drag_start, snap_manager, document, input, responses),
Self::None => {}
}
}
@@ -90,6 +98,7 @@ impl ShapeGizmoHandlers {
/// Cleans up any state used by the gizmo handler.
pub fn cleanup(&mut self) {
match self {
Self::Line(h) => h.cleanup(),
Self::Star(h) => h.cleanup(),
Self::Polygon(h) => h.cleanup(),
Self::Arc(h) => h.cleanup(),
@@ -109,6 +118,7 @@ impl ShapeGizmoHandlers {
overlay_context: &mut OverlayContext,
) {
match self {
Self::Line(h) => h.overlays(document, layer, input, shape_editor, mouse_position, overlay_context),
Self::Star(h) => h.overlays(document, layer, input, shape_editor, mouse_position, overlay_context),
Self::Polygon(h) => h.overlays(document, layer, input, shape_editor, mouse_position, overlay_context),
Self::Arc(h) => h.overlays(document, layer, input, shape_editor, mouse_position, overlay_context),
@@ -127,6 +137,7 @@ impl ShapeGizmoHandlers {
overlay_context: &mut OverlayContext,
) {
match self {
Self::Line(h) => h.dragging_overlays(document, input, shape_editor, mouse_position, overlay_context),
Self::Star(h) => h.dragging_overlays(document, input, shape_editor, mouse_position, overlay_context),
Self::Polygon(h) => h.dragging_overlays(document, input, shape_editor, mouse_position, overlay_context),
Self::Arc(h) => h.dragging_overlays(document, input, shape_editor, mouse_position, overlay_context),
@@ -137,6 +148,7 @@ impl ShapeGizmoHandlers {
pub fn gizmo_cursor_icon(&self) -> Option<MouseCursorIcon> {
match self {
Self::Line(h) => h.mouse_cursor_icon(),
Self::Star(h) => h.mouse_cursor_icon(),
Self::Polygon(h) => h.mouse_cursor_icon(),
Self::Arc(h) => h.mouse_cursor_icon(),
@@ -168,6 +180,10 @@ impl GizmoManager {
///
/// Returns `None` if the given layer does not represent a shape with a registered gizmo.
pub fn detect_shape_handler(layer: LayerNodeIdentifier, document: &DocumentMessageHandler) -> Option<ShapeGizmoHandlers> {
// Line
if graph_modification_utils::get_line_id(layer, &document.network_interface).is_some() {
return Some(ShapeGizmoHandlers::Line(LineGizmoHandler::default()));
}
// Star
if graph_modification_utils::get_star_id(layer, &document.network_interface).is_some() {
return Some(ShapeGizmoHandlers::Star(StarGizmoHandler::default()));
@@ -196,12 +212,12 @@ impl GizmoManager {
/// Called every frame to check selected layers and update the active shape gizmo, if hovered.
///
/// Also groups all shape layers with the same kind of gizmo to support overlays for multi-shape editing.
pub fn handle_actions(&mut self, mouse_position: DVec2, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
pub fn handle_actions(&mut self, mouse_position: DVec2, document: &DocumentMessageHandler) {
let mut handlers_layer: Vec<(ShapeGizmoHandlers, Vec<LayerNodeIdentifier>)> = Vec::new();
for layer in document.network_interface.selected_nodes().selected_visible_and_unlocked_layers(&document.network_interface) {
if let Some(mut handler) = Self::detect_shape_handler(layer, document) {
handler.handle_state(layer, mouse_position, document, responses);
handler.handle_state(layer, mouse_position, document);
let is_hovered = handler.is_any_gizmo_hovered();
if is_hovered {
@@ -239,9 +255,9 @@ impl GizmoManager {
}
/// Passes drag update data to the active gizmo to update shape parameters live.
pub fn handle_update(&mut self, drag_start: DVec2, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) {
pub fn handle_update(&mut self, drag_start: DVec2, snap_manager: &mut SnapManager, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) {
if let Some(handle) = &mut self.active_shape_handler {
handle.handle_update(drag_start, document, input, responses);
handle.handle_update(drag_start, snap_manager, document, input, responses);
}
}

View File

@@ -1,11 +1,10 @@
use crate::consts::GIZMO_HIDE_THRESHOLD;
use crate::messages::frontend::utility_types::MouseCursorIcon;
use crate::messages::message::Message;
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::prelude::Responses;
use crate::messages::prelude::{DocumentMessageHandler, InputPreprocessorMessageHandler, NodeGraphMessage};
use crate::messages::prelude::{FrontendMessage, Responses};
use crate::messages::tool::common_functionality::graph_modification_utils::{self, get_arc_id, get_stroke_width};
use crate::messages::tool::common_functionality::shapes::shape_utility::{extract_arc_parameters, extract_circle_radius};
use glam::{DAffine2, DVec2};
@@ -77,7 +76,7 @@ impl RadiusHandle {
stroke_width + extra_spacing
}
pub fn handle_actions(&mut self, layer: LayerNodeIdentifier, document: &DocumentMessageHandler, mouse_position: DVec2, responses: &mut VecDeque<Message>) {
pub fn handle_actions(&mut self, layer: LayerNodeIdentifier, document: &DocumentMessageHandler, mouse_position: DVec2) {
match &self.handle_state {
RadiusHandleState::Inactive => {
let Some(radius) = extract_circle_radius(layer, document).or(extract_arc_parameters(Some(layer), document).map(|(r, _, _, _)| r)) else {
@@ -99,8 +98,6 @@ impl RadiusHandle {
self.angle = angle;
self.update_state(RadiusHandleState::Hover);
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::EWResize });
}
}
RadiusHandleState::Dragging | RadiusHandleState::Hover => {}

View File

@@ -0,0 +1,139 @@
use crate::consts::BOUNDS_SELECT_THRESHOLD;
use crate::messages::message::Message;
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::prelude::Responses;
use crate::messages::prelude::{DocumentMessageHandler, InputPreprocessorMessageHandler, NodeGraphMessage};
use crate::messages::tool::common_functionality::graph_modification_utils::{self};
use crate::messages::tool::common_functionality::shapes::LineEnd;
use crate::messages::tool::common_functionality::shapes::shape_utility::{extract_line_parameters, generate_line};
use crate::messages::tool::common_functionality::snapping::{SnapData, SnapManager};
use crate::messages::tool::tool_messages::tool_prelude::Key;
use glam::DVec2;
use graph_craft::document::NodeInput;
use graph_craft::document::value::TaggedValue;
use std::collections::VecDeque;
#[derive(Clone, Debug, Default, PartialEq)]
pub enum LineEndPointHandleState {
#[default]
Inactive,
Hover,
Dragging,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct LineEndPointHandle {
pub layer: Option<LayerNodeIdentifier>,
pub handle_state: LineEndPointHandleState,
end_point: LineEnd,
drag_start: DVec2,
drag_current: DVec2,
angle: f64,
}
impl LineEndPointHandle {
pub fn cleanup(&mut self) {
self.handle_state = LineEndPointHandleState::Inactive;
self.layer = None;
}
pub fn hovered(&self) -> bool {
self.handle_state == LineEndPointHandleState::Hover
}
pub fn is_dragging(&self) -> bool {
self.handle_state == LineEndPointHandleState::Dragging
}
pub fn update_state(&mut self, state: LineEndPointHandleState) {
self.handle_state = state;
}
pub fn handle_actions(&mut self, layer: LayerNodeIdentifier, document: &DocumentMessageHandler, mouse_position: DVec2) {
match &self.handle_state {
LineEndPointHandleState::Inactive => {
if self.clicked_on_line_endpoints(layer, document, mouse_position) {
self.drag_current = mouse_position;
self.update_state(LineEndPointHandleState::Hover);
}
}
_ => {}
}
}
pub fn overlays(&self, selected_line_layer: Option<LayerNodeIdentifier>, document: &DocumentMessageHandler, overlay_context: &mut OverlayContext) {
let Some(layer) = selected_line_layer.or(self.layer) else { return };
let Some((start, end)) = extract_line_parameters(Some(layer), document) else { return };
let [viewport_start, viewport_end] = [start, end].map(|point| document.metadata().transform_to_viewport(layer).transform_point2(point));
overlay_context.line(viewport_start, viewport_end, None, None);
if !start.abs_diff_eq(end, f64::EPSILON * 1000.) {
overlay_context.square(viewport_start, Some(6.), None, None);
overlay_context.square(viewport_end, Some(6.), None, None);
}
}
pub fn update_endpoint_position(&mut self, document: &DocumentMessageHandler, snap: &mut SnapManager, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) {
let Some(layer) = self.layer else { return };
self.drag_current = input.mouse.position;
let ignore = [layer];
let snap_data = SnapData::ignore(document, input, &ignore);
let to_document = document.metadata().transform_to_document(layer);
let (mut document_points, angle) = generate_line(
self.angle,
to_document.transform_point2(self.drag_start),
input.mouse.position,
snap,
snap_data,
input.keyboard.key(Key::Shift),
input.keyboard.key(Key::Control),
input.keyboard.key(Key::Alt),
);
self.angle = angle;
if self.end_point == LineEnd::Start {
document_points.swap(0, 1);
}
let Some(node_id) = graph_modification_utils::get_line_id(layer, &document.network_interface) else {
return;
};
responses.add(NodeGraphMessage::SetInput {
input_connector: InputConnector::node(node_id, 1),
input: NodeInput::value(TaggedValue::DVec2(to_document.inverse().transform_point2(document_points[0])), false),
});
responses.add(NodeGraphMessage::SetInput {
input_connector: InputConnector::node(node_id, 2),
input: NodeInput::value(TaggedValue::DVec2(to_document.inverse().transform_point2(document_points[1])), false),
});
responses.add(NodeGraphMessage::RunDocumentGraph);
}
pub fn clicked_on_line_endpoints(&mut self, layer: LayerNodeIdentifier, document: &DocumentMessageHandler, drag_start: DVec2) -> bool {
let Some((document_start, document_end)) = extract_line_parameters(Some(layer), document) else {
return false;
};
let transform = document.metadata().transform_to_viewport(layer);
let viewport_x = transform.transform_vector2(DVec2::X).normalize_or_zero() * BOUNDS_SELECT_THRESHOLD;
let viewport_y = transform.transform_vector2(DVec2::Y).normalize_or_zero() * BOUNDS_SELECT_THRESHOLD;
let threshold_x = transform.inverse().transform_vector2(viewport_x).length();
let threshold_y = transform.inverse().transform_vector2(viewport_y).length();
let [start, end] = [document_start, document_end].map(|point| transform.transform_point2(point));
let start_click = (drag_start.y - start.y).abs() < threshold_y && (drag_start.x - start.x).abs() < threshold_x;
let end_click = (drag_start.y - end.y).abs() < threshold_y && (drag_start.x - end.x).abs() < threshold_x;
if start_click || end_click {
self.end_point = if end_click { LineEnd::End } else { LineEnd::Start };
self.drag_start = if end_click { document_start } else { document_end };
self.layer = Some(layer);
return true;
}
false
}
}

View File

@@ -1,4 +1,5 @@
pub mod circle_arc_radius_handle;
pub mod line_endpoint_handle;
pub mod number_of_points_dial;
pub mod point_radius_handle;
pub mod sweep_angle_gizmo;

View File

@@ -1,11 +1,10 @@
use crate::consts::{GIZMO_HIDE_THRESHOLD, NUMBER_OF_POINTS_DIAL_SPOKE_EXTENSION, NUMBER_OF_POINTS_DIAL_SPOKE_LENGTH, POINT_RADIUS_HANDLE_SEGMENT_THRESHOLD};
use crate::messages::frontend::utility_types::MouseCursorIcon;
use crate::messages::message::Message;
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::prelude::Responses;
use crate::messages::prelude::{DocumentMessageHandler, FrontendMessage, InputPreprocessorMessageHandler, NodeGraphMessage};
use crate::messages::prelude::{DocumentMessageHandler, InputPreprocessorMessageHandler, NodeGraphMessage};
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::{extract_polygon_parameters, inside_polygon, inside_star, polygon_outline, polygon_vertex_position, star_outline};
@@ -49,7 +48,7 @@ impl NumberOfPointsDial {
self.handle_state == NumberOfPointsDialState::Dragging
}
pub fn handle_actions(&mut self, layer: LayerNodeIdentifier, mouse_position: DVec2, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
pub fn handle_actions(&mut self, layer: LayerNodeIdentifier, mouse_position: DVec2, document: &DocumentMessageHandler) {
match &self.handle_state {
NumberOfPointsDialState::Inactive => {
// Star
@@ -63,7 +62,6 @@ impl NumberOfPointsDial {
self.layer = Some(layer);
self.initial_points = sides;
self.update_state(NumberOfPointsDialState::Hover);
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::EWResize });
}
}
@@ -78,7 +76,6 @@ impl NumberOfPointsDial {
self.layer = Some(layer);
self.initial_points = sides;
self.update_state(NumberOfPointsDialState::Hover);
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::EWResize });
}
}
}
@@ -91,7 +88,6 @@ impl NumberOfPointsDial {
if mouse_position.distance(center) > NUMBER_OF_POINTS_DIAL_SPOKE_LENGTH && matches!(&self.handle_state, NumberOfPointsDialState::Hover) {
self.update_state(NumberOfPointsDialState::Inactive);
self.layer = None;
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Default });
}
}
}

View File

@@ -1,10 +1,8 @@
use crate::consts::GIZMO_HIDE_THRESHOLD;
use crate::consts::{COLOR_OVERLAY_RED, POINT_RADIUS_HANDLE_SNAP_THRESHOLD};
use crate::messages::frontend::utility_types::MouseCursorIcon;
use crate::messages::message::Message;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::{overlays::utility_types::OverlayContext, utility_types::network_interface::InputConnector};
use crate::messages::prelude::FrontendMessage;
use crate::messages::prelude::Responses;
use crate::messages::prelude::{DocumentMessageHandler, InputPreprocessorMessageHandler, NodeGraphMessage};
use crate::messages::tool::common_functionality::graph_modification_utils::{self, NodeGraphLayer};
@@ -54,7 +52,7 @@ impl PointRadiusHandle {
self.handle_state = state;
}
pub fn handle_actions(&mut self, layer: LayerNodeIdentifier, document: &DocumentMessageHandler, mouse_position: DVec2, responses: &mut VecDeque<Message>) {
pub fn handle_actions(&mut self, layer: LayerNodeIdentifier, document: &DocumentMessageHandler, mouse_position: DVec2) {
match &self.handle_state {
PointRadiusHandleState::Inactive => {
// Draw the point handle gizmo for the star shape
@@ -77,7 +75,6 @@ impl PointRadiusHandle {
self.point = i;
self.snap_radii = Self::calculate_snap_radii(document, layer, radius_index);
self.initial_radius = radius;
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Default });
self.update_state(PointRadiusHandleState::Hover);
return;
@@ -105,7 +102,6 @@ impl PointRadiusHandle {
self.snap_radii.clear();
self.initial_radius = radius;
self.update_state(PointRadiusHandleState::Hover);
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Default });
return;
}
}

View File

@@ -8,6 +8,7 @@ use crate::messages::tool::common_functionality::gizmos::shape_gizmos::circle_ar
use crate::messages::tool::common_functionality::gizmos::shape_gizmos::sweep_angle_gizmo::{SweepAngleGizmo, SweepAngleGizmoState};
use crate::messages::tool::common_functionality::graph_modification_utils;
use crate::messages::tool::common_functionality::shapes::shape_utility::{ShapeGizmoHandler, arc_outline};
use crate::messages::tool::common_functionality::snapping::SnapManager;
use crate::messages::tool::tool_messages::tool_prelude::*;
use glam::DAffine2;
use graph_craft::document::NodeInput;
@@ -28,9 +29,9 @@ impl ArcGizmoHandler {
}
impl ShapeGizmoHandler for ArcGizmoHandler {
fn handle_state(&mut self, selected_shape_layer: LayerNodeIdentifier, mouse_position: DVec2, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
fn handle_state(&mut self, selected_shape_layer: LayerNodeIdentifier, mouse_position: DVec2, document: &DocumentMessageHandler) {
self.sweep_angle_gizmo.handle_actions(selected_shape_layer, document, mouse_position);
self.arc_radius_handle.handle_actions(selected_shape_layer, document, mouse_position, responses);
self.arc_radius_handle.handle_actions(selected_shape_layer, document, mouse_position);
}
fn is_any_gizmo_hovered(&self) -> bool {
@@ -54,7 +55,7 @@ impl ShapeGizmoHandler for ArcGizmoHandler {
}
}
fn handle_update(&mut self, drag_start: DVec2, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) {
fn handle_update(&mut self, drag_start: DVec2, _snap_manager: &mut SnapManager, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) {
if self.sweep_angle_gizmo.is_dragging_or_snapped() {
self.sweep_angle_gizmo.update_arc(document, input, responses);
}

View File

@@ -7,6 +7,7 @@ use crate::messages::tool::common_functionality::gizmos::shape_gizmos::circle_ar
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, ShapeToolModifierKey};
use crate::messages::tool::common_functionality::snapping::SnapManager;
use crate::messages::tool::tool_messages::shape_tool::ShapeToolData;
use crate::messages::tool::tool_messages::tool_prelude::*;
use glam::DAffine2;
@@ -23,8 +24,8 @@ impl ShapeGizmoHandler for CircleGizmoHandler {
self.circle_radius_handle.hovered()
}
fn handle_state(&mut self, selected_circle_layer: LayerNodeIdentifier, mouse_position: DVec2, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
self.circle_radius_handle.handle_actions(selected_circle_layer, document, mouse_position, responses);
fn handle_state(&mut self, selected_circle_layer: LayerNodeIdentifier, mouse_position: DVec2, document: &DocumentMessageHandler) {
self.circle_radius_handle.handle_actions(selected_circle_layer, document, mouse_position);
}
fn handle_click(&mut self) {
@@ -33,7 +34,7 @@ impl ShapeGizmoHandler for CircleGizmoHandler {
}
}
fn handle_update(&mut self, drag_start: DVec2, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) {
fn handle_update(&mut self, drag_start: DVec2, _snap_manager: &mut SnapManager, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) {
if self.circle_radius_handle.is_dragging() {
self.circle_radius_handle.update_inner_radius(document, input, responses, drag_start);
}

View File

@@ -1,12 +1,14 @@
use super::shape_utility::ShapeToolModifierKey;
use crate::consts::{BOUNDS_SELECT_THRESHOLD, LINE_ROTATE_SNAP_ANGLE};
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::line_endpoint_handle::{LineEndPointHandle, LineEndPointHandleState};
use crate::messages::tool::common_functionality::graph_modification_utils;
pub use crate::messages::tool::common_functionality::graph_modification_utils::NodeGraphLayer;
use crate::messages::tool::common_functionality::snapping::{SnapCandidatePoint, SnapConstraint, SnapData, SnapTypeConfiguration};
use crate::messages::tool::common_functionality::shape_editor::ShapeState;
use crate::messages::tool::common_functionality::shapes::shape_utility::{ShapeGizmoHandler, generate_line};
use crate::messages::tool::common_functionality::snapping::{SnapData, SnapManager};
use crate::messages::tool::tool_messages::shape_tool::ShapeToolData;
use crate::messages::tool::tool_messages::tool_prelude::*;
use glam::DVec2;
@@ -14,6 +16,77 @@ use graph_craft::document::NodeInput;
use graph_craft::document::value::TaggedValue;
use std::collections::VecDeque;
#[derive(Clone, Debug, Default)]
pub struct LineGizmoHandler {
line_endpoint_handle: LineEndPointHandle,
}
impl LineGizmoHandler {
pub fn new() -> Self {
Self { ..Default::default() }
}
}
impl ShapeGizmoHandler for LineGizmoHandler {
fn handle_state(&mut self, selected_shape_layer: LayerNodeIdentifier, mouse_position: DVec2, document: &DocumentMessageHandler) {
self.line_endpoint_handle.handle_actions(selected_shape_layer, document, mouse_position);
}
fn is_any_gizmo_hovered(&self) -> bool {
self.line_endpoint_handle.hovered()
}
fn handle_click(&mut self) {
// If hovering over both the gizmos give priority to sweep angle gizmo
if self.line_endpoint_handle.hovered() {
self.line_endpoint_handle.update_state(LineEndPointHandleState::Dragging);
return;
}
}
fn handle_update(&mut self, _drag_start: DVec2, snap_manager: &mut SnapManager, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) {
if self.line_endpoint_handle.is_dragging() {
self.line_endpoint_handle.update_endpoint_position(document, snap_manager, input, responses);
}
}
fn dragging_overlays(
&self,
document: &DocumentMessageHandler,
_input: &InputPreprocessorMessageHandler,
_shape_editor: &mut &mut crate::messages::tool::common_functionality::shape_editor::ShapeState,
_mouse_position: DVec2,
overlay_context: &mut crate::messages::portfolio::document::overlays::utility_types::OverlayContext,
) {
if self.line_endpoint_handle.is_dragging() {
self.line_endpoint_handle.overlays(None, document, overlay_context);
}
}
fn overlays(
&self,
document: &DocumentMessageHandler,
selected_shape_layer: Option<LayerNodeIdentifier>,
_input: &InputPreprocessorMessageHandler,
_shape_editor: &mut &mut ShapeState,
_mouse_position: DVec2,
overlay_context: &mut OverlayContext,
) {
self.line_endpoint_handle.overlays(selected_shape_layer, document, overlay_context);
}
fn mouse_cursor_icon(&self) -> Option<MouseCursorIcon> {
if self.line_endpoint_handle.hovered() {
return Some(MouseCursorIcon::Default);
}
None
}
fn cleanup(&mut self) {
self.line_endpoint_handle.cleanup();
}
}
#[derive(Clone, PartialEq, Debug, Default)]
pub enum LineEnd {
#[default]
@@ -24,12 +97,8 @@ pub enum LineEnd {
#[derive(Clone, Debug, Default)]
pub struct LineToolData {
pub drag_start: DVec2,
pub drag_current: DVec2,
pub angle: f64,
pub weight: f64,
pub selected_layers_with_position: HashMap<LayerNodeIdentifier, [DVec2; 2]>,
pub editing_layer: Option<LayerNodeIdentifier>,
pub dragging_endpoint: Option<LineEnd>,
}
#[derive(Default)]
@@ -55,16 +124,23 @@ impl Line {
) {
let [center, snap_angle, lock_angle] = modifier;
shape_tool_data.line_data.drag_current = ipp.mouse.position;
let keyboard = &ipp.keyboard;
let ignore = [layer];
let snap_data = SnapData::ignore(document, ipp, &ignore);
let mut document_points = generate_line(shape_tool_data, snap_data, keyboard.key(lock_angle), keyboard.key(snap_angle), keyboard.key(center));
let (document_points, angle) = generate_line(
shape_tool_data.line_data.angle,
shape_tool_data.data.drag_start,
ipp.mouse.position,
&mut shape_tool_data.data.snap_manager,
snap_data,
keyboard.key(lock_angle),
keyboard.key(snap_angle),
keyboard.key(center),
);
if shape_tool_data.line_data.dragging_endpoint == Some(LineEnd::Start) {
document_points.swap(0, 1);
}
shape_tool_data.line_data.angle = angle;
let to_document = document.metadata().transform_to_document(layer);
let Some(node_id) = graph_modification_utils::get_line_id(layer, &document.network_interface) else {
return;
@@ -72,131 +148,14 @@ impl Line {
responses.add(NodeGraphMessage::SetInput {
input_connector: InputConnector::node(node_id, 1),
input: NodeInput::value(TaggedValue::DVec2(document_points[0]), false),
input: NodeInput::value(TaggedValue::DVec2(to_document.inverse().transform_point2(document_points[0])), false),
});
responses.add(NodeGraphMessage::SetInput {
input_connector: InputConnector::node(node_id, 2),
input: NodeInput::value(TaggedValue::DVec2(document_points[1]), false),
input: NodeInput::value(TaggedValue::DVec2(to_document.inverse().transform_point2(document_points[1])), false),
});
responses.add(NodeGraphMessage::RunDocumentGraph);
}
pub fn overlays(document: &DocumentMessageHandler, shape_tool_data: &mut ShapeToolData, overlay_context: &mut OverlayContext) {
shape_tool_data.line_data.selected_layers_with_position = document
.network_interface
.selected_nodes()
.selected_visible_and_unlocked_layers(&document.network_interface)
.filter_map(|layer| {
let node_inputs = NodeGraphLayer::new(layer, &document.network_interface).find_node_inputs("Line")?;
let (Some(&TaggedValue::DVec2(start)), Some(&TaggedValue::DVec2(end))) = (node_inputs[1].as_value(), node_inputs[2].as_value()) else {
return None;
};
let [viewport_start, viewport_end] = [start, end].map(|point| document.metadata().transform_to_viewport(layer).transform_point2(point));
if !start.abs_diff_eq(end, f64::EPSILON * 1000.) {
overlay_context.line(viewport_start, viewport_end, None, None);
overlay_context.square(viewport_start, Some(6.), None, None);
overlay_context.square(viewport_end, Some(6.), None, None);
}
Some((layer, [start, end]))
})
.collect::<HashMap<LayerNodeIdentifier, [DVec2; 2]>>();
}
}
fn generate_line(tool_data: &mut ShapeToolData, snap_data: SnapData, lock_angle: bool, snap_angle: bool, center: bool) -> [DVec2; 2] {
let document_to_viewport = snap_data.document.metadata().document_to_viewport;
let mut document_points = [tool_data.data.drag_start, document_to_viewport.inverse().transform_point2(tool_data.line_data.drag_current)];
let mut angle = -(document_points[1] - document_points[0]).angle_to(DVec2::X);
let mut line_length = (document_points[1] - document_points[0]).length();
if lock_angle {
angle = tool_data.line_data.angle;
} else if snap_angle {
let snap_resolution = LINE_ROTATE_SNAP_ANGLE.to_radians();
angle = (angle / snap_resolution).round() * snap_resolution;
}
tool_data.line_data.angle = angle;
if lock_angle {
let angle_vec = DVec2::new(angle.cos(), angle.sin());
line_length = (document_points[1] - document_points[0]).dot(angle_vec);
}
document_points[1] = document_points[0] + line_length * DVec2::new(angle.cos(), angle.sin());
let constrained = snap_angle || lock_angle;
let snap = &mut tool_data.data.snap_manager;
let near_point = SnapCandidatePoint::handle_neighbors(document_points[1], [tool_data.data.drag_start]);
let far_point = SnapCandidatePoint::handle_neighbors(2. * document_points[0] - document_points[1], [tool_data.data.drag_start]);
let config = SnapTypeConfiguration::default();
if constrained {
let constraint = SnapConstraint::Line {
origin: document_points[0],
direction: document_points[1] - document_points[0],
};
if center {
let snapped = snap.constrained_snap(&snap_data, &near_point, constraint, config);
let snapped_far = snap.constrained_snap(&snap_data, &far_point, constraint, config);
let best = if snapped_far.other_snap_better(&snapped) { snapped } else { snapped_far };
document_points[1] = document_points[0] * 2. - best.snapped_point_document;
document_points[0] = best.snapped_point_document;
snap.update_indicator(best);
} else {
let snapped = snap.constrained_snap(&snap_data, &near_point, constraint, config);
document_points[1] = snapped.snapped_point_document;
snap.update_indicator(snapped);
}
} else if center {
let snapped = snap.free_snap(&snap_data, &near_point, config);
let snapped_far = snap.free_snap(&snap_data, &far_point, config);
let best = if snapped_far.other_snap_better(&snapped) { snapped } else { snapped_far };
document_points[1] = document_points[0] * 2. - best.snapped_point_document;
document_points[0] = best.snapped_point_document;
snap.update_indicator(best);
} else {
let snapped = snap.free_snap(&snap_data, &near_point, config);
document_points[1] = snapped.snapped_point_document;
snap.update_indicator(snapped);
}
document_points
}
pub fn clicked_on_line_endpoints(layer: LayerNodeIdentifier, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, shape_tool_data: &mut ShapeToolData) -> bool {
let Some(node_inputs) = NodeGraphLayer::new(layer, &document.network_interface).find_node_inputs("Line") else {
return false;
};
let (Some(&TaggedValue::DVec2(document_start)), Some(&TaggedValue::DVec2(document_end))) = (node_inputs[1].as_value(), node_inputs[2].as_value()) else {
return false;
};
let transform = document.metadata().transform_to_viewport(layer);
let viewport_x = transform.transform_vector2(DVec2::X).normalize_or_zero() * BOUNDS_SELECT_THRESHOLD;
let viewport_y = transform.transform_vector2(DVec2::Y).normalize_or_zero() * BOUNDS_SELECT_THRESHOLD;
let threshold_x = transform.inverse().transform_vector2(viewport_x).length();
let threshold_y = transform.inverse().transform_vector2(viewport_y).length();
let drag_start = input.mouse.position;
let [start, end] = [document_start, document_end].map(|point| transform.transform_point2(point));
let start_click = (drag_start.y - start.y).abs() < threshold_y && (drag_start.x - start.x).abs() < threshold_x;
let end_click = (drag_start.y - end.y).abs() < threshold_y && (drag_start.x - end.x).abs() < threshold_x;
if start_click || end_click {
shape_tool_data.line_data.dragging_endpoint = Some(if end_click { LineEnd::End } else { LineEnd::Start });
shape_tool_data.data.drag_start = if end_click { document_start } else { document_end };
shape_tool_data.line_data.editing_layer = Some(layer);
return true;
}
false
}
#[cfg(test)]

View File

@@ -14,6 +14,7 @@ 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::common_functionality::snapping::SnapManager;
use crate::messages::tool::tool_messages::tool_prelude::*;
use glam::DAffine2;
use graph_craft::document::NodeInput;
@@ -31,9 +32,9 @@ impl ShapeGizmoHandler for PolygonGizmoHandler {
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_state(&mut self, selected_star_layer: LayerNodeIdentifier, mouse_position: DVec2, document: &DocumentMessageHandler) {
self.number_of_points_dial.handle_actions(selected_star_layer, mouse_position, document);
self.point_radius_handle.handle_actions(selected_star_layer, document, mouse_position);
}
fn handle_click(&mut self) {
@@ -47,7 +48,7 @@ impl ShapeGizmoHandler for PolygonGizmoHandler {
}
}
fn handle_update(&mut self, drag_start: DVec2, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) {
fn handle_update(&mut self, drag_start: DVec2, _snap_manager: &mut SnapManager, 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);
}

View File

@@ -1,5 +1,5 @@
use super::ShapeToolData;
use crate::consts::{ARC_SWEEP_GIZMO_RADIUS, ARC_SWEEP_GIZMO_TEXT_HEIGHT};
use crate::consts::{ARC_SWEEP_GIZMO_RADIUS, ARC_SWEEP_GIZMO_TEXT_HEIGHT, LINE_ROTATE_SNAP_ANGLE};
use crate::messages::frontend::utility_types::MouseCursorIcon;
use crate::messages::message::Message;
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
@@ -8,6 +8,7 @@ use crate::messages::portfolio::document::utility_types::network_interface::Inpu
use crate::messages::prelude::{DocumentMessageHandler, InputPreprocessorMessageHandler, NodeGraphMessage, Responses};
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::snapping::{SnapCandidatePoint, SnapConstraint, SnapData, SnapManager, SnapTypeConfiguration};
use crate::messages::tool::common_functionality::transformation_cage::BoundingBoxManager;
use crate::messages::tool::tool_messages::tool_prelude::Key;
use crate::messages::tool::utility_types::*;
@@ -85,7 +86,7 @@ pub trait ShapeGizmoHandler {
/// Called every frame to update the gizmo's interaction state based on the mouse position and selection.
///
/// This includes detecting hover states and preparing interaction flags or visual feedback (e.g., highlighting a hovered handle).
fn handle_state(&mut self, selected_shape_layers: LayerNodeIdentifier, mouse_position: DVec2, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>);
fn handle_state(&mut self, selected_shape_layers: LayerNodeIdentifier, mouse_position: DVec2, document: &DocumentMessageHandler);
/// Called when a mouse click occurs over the canvas and a gizmo handle is hovered.
///
@@ -96,7 +97,7 @@ pub trait ShapeGizmoHandler {
/// Called during a drag interaction to update the shape's parameters in real time.
///
/// For example, a handle might calculate the distance from the drag start to determine a new radius or update the number of points.
fn handle_update(&mut self, drag_start: DVec2, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>);
fn handle_update(&mut self, drag_start: DVec2, snap_manager: &mut SnapManager, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>);
/// Draws the static or hover-dependent overlays associated with the gizmo.
///
@@ -259,6 +260,18 @@ pub fn extract_arc_parameters(layer: Option<LayerNodeIdentifier>, document: &Doc
Some((radius, start_angle, sweep_angle, arc_type))
}
// Extract the node input values of an Line.
/// Returns an option of (start point, end point).
pub fn extract_line_parameters(layer: Option<LayerNodeIdentifier>, document: &DocumentMessageHandler) -> Option<(DVec2, DVec2)> {
let node_inputs = NodeGraphLayer::new(layer?, &document.network_interface).find_node_inputs("Line")?;
let (Some(&TaggedValue::DVec2(start)), Some(&TaggedValue::DVec2(end))) = (node_inputs.get(1)?.as_value(), node_inputs.get(2)?.as_value()) else {
return None;
};
Some((start, end))
}
/// Calculate the viewport positions of arc endpoints
pub fn arc_end_points(layer: Option<LayerNodeIdentifier>, document: &DocumentMessageHandler) -> Option<(DVec2, DVec2)> {
let (radius, start_angle, sweep_angle, _) = extract_arc_parameters(Some(layer?), document)?;
@@ -475,3 +488,72 @@ pub fn calculate_arc_text_transform(angle: f64, offset_angle: f64, center: DVec2
);
DAffine2::from_translation(text_texture_position + center)
}
pub fn generate_line(
previous_angle: f64,
drag_start: DVec2,
current_position: DVec2,
snap: &mut SnapManager,
snap_data: SnapData,
lock_angle: bool,
snap_angle: bool,
center: bool,
) -> ([DVec2; 2], f64) {
let document_to_viewport = snap_data.document.metadata().document_to_viewport;
let mut document_points = [drag_start, document_to_viewport.inverse().transform_point2(current_position)];
let mut angle = -(document_points[1] - document_points[0]).angle_to(DVec2::X);
let mut line_length = (document_points[1] - document_points[0]).length();
if lock_angle {
angle = previous_angle;
} else if snap_angle {
let snap_resolution = LINE_ROTATE_SNAP_ANGLE.to_radians();
angle = (angle / snap_resolution).round() * snap_resolution;
}
if lock_angle {
let angle_vec = DVec2::new(angle.cos(), angle.sin());
line_length = (document_points[1] - document_points[0]).dot(angle_vec);
}
document_points[1] = document_points[0] + line_length * DVec2::new(angle.cos(), angle.sin());
let constrained = snap_angle || lock_angle;
let near_point = SnapCandidatePoint::handle_neighbors(document_points[1], [drag_start]);
let far_point = SnapCandidatePoint::handle_neighbors(2. * document_points[0] - document_points[1], [drag_start]);
let config = SnapTypeConfiguration::default();
if constrained {
let constraint = SnapConstraint::Line {
origin: document_points[0],
direction: document_points[1] - document_points[0],
};
if center {
let snapped = snap.constrained_snap(&snap_data, &near_point, constraint, config);
let snapped_far = snap.constrained_snap(&snap_data, &far_point, constraint, config);
let best = if snapped_far.other_snap_better(&snapped) { snapped } else { snapped_far };
document_points[1] = document_points[0] * 2. - best.snapped_point_document;
document_points[0] = best.snapped_point_document;
snap.update_indicator(best);
} else {
let snapped = snap.constrained_snap(&snap_data, &near_point, constraint, config);
document_points[1] = snapped.snapped_point_document;
snap.update_indicator(snapped);
}
} else if center {
let snapped = snap.free_snap(&snap_data, &near_point, config);
let snapped_far = snap.free_snap(&snap_data, &far_point, config);
let best = if snapped_far.other_snap_better(&snapped) { snapped } else { snapped_far };
document_points[1] = document_points[0] * 2. - best.snapped_point_document;
document_points[0] = best.snapped_point_document;
snap.update_indicator(best);
} else {
let snapped = snap.free_snap(&snap_data, &near_point, config);
document_points[1] = snapped.snapped_point_document;
snap.update_indicator(snapped);
}
([document_points[0], document_points[1]], angle)
}

View File

@@ -10,6 +10,7 @@ use crate::messages::tool::common_functionality::gizmos::shape_gizmos::point_rad
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, star_outline};
use crate::messages::tool::common_functionality::snapping::SnapManager;
use crate::messages::tool::tool_messages::tool_prelude::*;
use core::f64;
use glam::DAffine2;
@@ -28,9 +29,9 @@ impl ShapeGizmoHandler for StarGizmoHandler {
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_state(&mut self, selected_star_layer: LayerNodeIdentifier, mouse_position: DVec2, document: &DocumentMessageHandler) {
self.number_of_points_dial.handle_actions(selected_star_layer, mouse_position, document);
self.point_radius_handle.handle_actions(selected_star_layer, document, mouse_position);
}
fn handle_click(&mut self) {
@@ -44,7 +45,7 @@ impl ShapeGizmoHandler for StarGizmoHandler {
}
}
fn handle_update(&mut self, drag_start: DVec2, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) {
fn handle_update(&mut self, drag_start: DVec2, _snap_manager: &mut SnapManager, 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);
}

View File

@@ -1,5 +1,5 @@
use super::tool_prelude::*;
use crate::consts::{DEFAULT_STROKE_WIDTH, SNAP_POINT_TOLERANCE};
use crate::consts::DEFAULT_STROKE_WIDTH;
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;
@@ -12,14 +12,14 @@ use crate::messages::tool::common_functionality::graph_modification_utils::NodeG
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::line_shape::{LineToolData, clicked_on_line_endpoints};
use crate::messages::tool::common_functionality::shapes::line_shape::LineToolData;
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::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 crate::messages::tool::common_functionality::utility_functions::{resize_bounds, rotate_bounds, skew_bounds, transforming_transform_cage};
use graph_craft::document::value::TaggedValue;
use graph_craft::document::{NodeId, NodeInput};
use graphene_std::Color;
@@ -315,12 +315,7 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for Shap
DecreaseSides,
NudgeSelectedLayers,
),
ShapeToolFsmState::Drawing(_)
| ShapeToolFsmState::ResizingBounds
| ShapeToolFsmState::DraggingLineEndpoints
| ShapeToolFsmState::RotatingBounds
| ShapeToolFsmState::ModifyingGizmo
| ShapeToolFsmState::SkewingBounds { .. } => {
ShapeToolFsmState::Drawing(_) | ShapeToolFsmState::ResizingBounds | ShapeToolFsmState::RotatingBounds | ShapeToolFsmState::ModifyingGizmo | ShapeToolFsmState::SkewingBounds { .. } => {
actions!(ShapeToolMessageDiscriminant;
DragStop,
Abort,
@@ -365,7 +360,6 @@ pub enum ShapeToolFsmState {
Drawing(ShapeType),
// Gizmos
DraggingLineEndpoints,
ModifyingGizmo,
// Transform cage
@@ -452,7 +446,6 @@ impl Fsm for ShapeToolFsmState {
document,
global_tool_data,
input,
preferences,
shape_editor,
..
}: &mut ToolActionMessageContext,
@@ -477,7 +470,7 @@ impl Fsm for ShapeToolFsmState {
.unwrap_or(input.mouse.position);
if matches!(self, Self::Ready(_)) && !input.keyboard.key(Key::Control) {
tool_data.gizmo_manager.handle_actions(mouse_position, document, responses);
tool_data.gizmo_manager.handle_actions(mouse_position, document);
tool_data.gizmo_manager.overlays(document, input, shape_editor, mouse_position, &mut overlay_context);
}
@@ -504,8 +497,6 @@ impl Fsm for ShapeToolFsmState {
anchor_overlays(document, &mut overlay_context);
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Crosshair });
} else if matches!(self, ShapeToolFsmState::Ready(_)) {
Line::overlays(document, tool_data, &mut overlay_context);
if all_selected_layers_line {
return self;
}
@@ -540,11 +531,8 @@ impl Fsm for ShapeToolFsmState {
responses.add(FrontendMessage::UpdateMouseCursor { cursor });
}
if matches!(self, ShapeToolFsmState::Drawing(_) | ShapeToolFsmState::DraggingLineEndpoints) {
Line::overlays(document, tool_data, &mut overlay_context);
if tool_options.shape_type == ShapeType::Circle {
tool_data.gizmo_manager.overlays(document, input, shape_editor, mouse_position, &mut overlay_context);
}
if matches!(self, ShapeToolFsmState::Drawing(_)) {
tool_data.gizmo_manager.overlays(document, input, shape_editor, mouse_position, &mut overlay_context);
}
self
@@ -657,8 +645,6 @@ impl Fsm for ShapeToolFsmState {
self
}
(ShapeToolFsmState::Ready(_), ShapeToolMessage::DragStart) => {
tool_data.line_data.drag_start = input.mouse.position;
// Snapped position in viewport space
let mouse_pos = tool_data
.data
@@ -667,8 +653,6 @@ impl Fsm for ShapeToolFsmState {
.map(|pos| document.metadata().document_to_viewport.transform_point2(pos))
.unwrap_or(input.mouse.position);
tool_data.line_data.drag_current = mouse_pos;
if tool_data.gizmo_manager.handle_click() && !input.keyboard.key(Key::Accel) {
tool_data.data.drag_start = document.metadata().document_to_viewport.inverse().transform_point2(mouse_pos);
responses.add(DocumentMessage::StartTransaction);
@@ -684,20 +668,6 @@ impl Fsm for ShapeToolFsmState {
return ShapeToolFsmState::ModifyingGizmo;
}
// If clicked on endpoints of a selected line, drag its endpoints
if let Some((layer, _, _)) = closest_point(
document,
mouse_pos,
SNAP_POINT_TOLERANCE,
document.network_interface.selected_nodes().selected_visible_and_unlocked_layers(&document.network_interface),
|_| false,
preferences,
) {
if clicked_on_line_endpoints(layer, document, input, tool_data) && !input.keyboard.key(Key::Control) {
return ShapeToolFsmState::DraggingLineEndpoints;
}
}
let (resize, rotate, skew) = transforming_transform_cage(document, &mut tool_data.bounding_box_manager, input, responses, &mut tool_data.layers_dragging, None);
if !input.keyboard.key(Key::Control) {
@@ -773,10 +743,8 @@ impl Fsm for ShapeToolFsmState {
}
ShapeType::Line => {
tool_data.line_data.weight = tool_options.line_weight;
tool_data.line_data.editing_layer = Some(layer);
}
}
tool_options.stroke.apply_stroke(tool_options.line_weight, layer, defered_responses);
tool_options.stroke.apply_stroke(tool_options.line_weight, layer, defered_responses);
tool_data.data.layer = Some(layer);
@@ -809,21 +777,15 @@ impl Fsm for ShapeToolFsmState {
self
}
(ShapeToolFsmState::DraggingLineEndpoints, ShapeToolMessage::PointerMove { modifier }) => {
let Some(layer) = tool_data.line_data.editing_layer else {
return ShapeToolFsmState::Ready(tool_data.current_shape);
};
Line::update_shape(document, input, layer, tool_data, modifier, responses);
// Auto-panning
(ShapeToolFsmState::ModifyingGizmo, ShapeToolMessage::PointerMove { modifier }) => {
tool_data
.gizmo_manager
.handle_update(tool_data.data.viewport_drag_start(document), &mut tool_data.data.snap_manager, document, input, responses);
let messages = [ShapeToolMessage::PointerOutsideViewport { modifier }.into(), ShapeToolMessage::PointerMove { modifier }.into()];
tool_data.auto_panning.setup_by_mouse_position(input, &messages, responses);
self
}
(ShapeToolFsmState::ModifyingGizmo, ShapeToolMessage::PointerMove { .. }) => {
tool_data.gizmo_manager.handle_update(tool_data.data.viewport_drag_start(document), document, input, responses);
responses.add(OverlaysMessage::Draw);
ShapeToolFsmState::ModifyingGizmo
@@ -921,12 +883,7 @@ impl Fsm for ShapeToolFsmState {
self
}
(
ShapeToolFsmState::Drawing(_)
| ShapeToolFsmState::DraggingLineEndpoints
| ShapeToolFsmState::ResizingBounds
| ShapeToolFsmState::RotatingBounds
| ShapeToolFsmState::SkewingBounds { .. }
| ShapeToolFsmState::ModifyingGizmo,
ShapeToolFsmState::Drawing(_) | ShapeToolFsmState::ResizingBounds | ShapeToolFsmState::RotatingBounds | ShapeToolFsmState::SkewingBounds { .. } | ShapeToolFsmState::ModifyingGizmo,
ShapeToolMessage::DragStop,
) => {
input.mouse.finish_transaction(tool_data.data.drag_start, responses);
@@ -938,24 +895,16 @@ impl Fsm for ShapeToolFsmState {
bounds.original_transforms.clear();
}
tool_data.line_data.dragging_endpoint = None;
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Crosshair });
ShapeToolFsmState::Ready(tool_data.current_shape)
}
(
ShapeToolFsmState::Drawing(_)
| ShapeToolFsmState::DraggingLineEndpoints
| ShapeToolFsmState::ResizingBounds
| ShapeToolFsmState::RotatingBounds
| ShapeToolFsmState::SkewingBounds { .. }
| ShapeToolFsmState::ModifyingGizmo,
ShapeToolFsmState::Drawing(_) | ShapeToolFsmState::ResizingBounds | ShapeToolFsmState::RotatingBounds | ShapeToolFsmState::SkewingBounds { .. } | ShapeToolFsmState::ModifyingGizmo,
ShapeToolMessage::Abort,
) => {
responses.add(DocumentMessage::AbortTransaction);
tool_data.data.cleanup(responses);
tool_data.line_data.dragging_endpoint = None;
tool_data.gizmo_manager.handle_cleanup();
@@ -1066,14 +1015,6 @@ fn update_dynamic_hints(state: &ShapeToolFsmState, responses: &mut VecDeque<Mess
HintData(common_hint_group)
}
ShapeToolFsmState::DraggingLineEndpoints => HintData(vec![
HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]),
HintGroup(vec![
HintInfo::keys([Key::Shift], "15° Increments"),
HintInfo::keys([Key::Alt], "From Center"),
HintInfo::keys([Key::Control], "Lock Angle"),
]),
]),
ShapeToolFsmState::ResizingBounds => HintData(vec![
HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]),
HintGroup(vec![HintInfo::keys([Key::Alt], "From Pivot"), HintInfo::keys([Key::Shift], "Preserve Aspect Ratio")]),