added multi select changed added gizmo context

This commit is contained in:
0SlowPoke0
2025-09-04 13:36:25 +05:30
parent 9ac766b824
commit 5ab6f2e5ae
17 changed files with 500 additions and 270 deletions
@@ -1,17 +1,16 @@
use crate::messages::frontend::utility_types::MouseCursorIcon; 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::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::prelude::{DocumentMessageHandler, InputPreprocessorMessageHandler}; use crate::messages::prelude::DocumentMessageHandler;
use crate::messages::tool::common_functionality::graph_modification_utils; use crate::messages::tool::common_functionality::graph_modification_utils;
use crate::messages::tool::common_functionality::shape_editor::ShapeState; use crate::messages::tool::common_functionality::operations::circular_repeat::CircularRepeatGizmoHandler;
use crate::messages::tool::common_functionality::shapes::arc_shape::ArcGizmoHandler; 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::circle_shape::CircleGizmoHandler;
use crate::messages::tool::common_functionality::shapes::polygon_shape::PolygonGizmoHandler; 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::shape_utility::{GizmoContext, ShapeGizmoHandler};
use crate::messages::tool::common_functionality::shapes::star_shape::StarGizmoHandler; use crate::messages::tool::common_functionality::shapes::star_shape::StarGizmoHandler;
use glam::DVec2; use glam::DVec2;
use std::collections::VecDeque; use std::collections::HashMap;
/// A unified enum wrapper around all available shape-specific gizmo handlers. /// A unified enum wrapper around all available shape-specific gizmo handlers.
/// ///
@@ -28,6 +27,7 @@ pub enum ShapeGizmoHandlers {
Polygon(PolygonGizmoHandler), Polygon(PolygonGizmoHandler),
Arc(ArcGizmoHandler), Arc(ArcGizmoHandler),
Circle(CircleGizmoHandler), Circle(CircleGizmoHandler),
CircularRepeat(CircularRepeatGizmoHandler),
} }
impl ShapeGizmoHandlers { impl ShapeGizmoHandlers {
@@ -39,17 +39,19 @@ impl ShapeGizmoHandlers {
Self::Polygon(_) => "polygon", Self::Polygon(_) => "polygon",
Self::Arc(_) => "arc", Self::Arc(_) => "arc",
Self::Circle(_) => "circle", Self::Circle(_) => "circle",
Self::CircularRepeat(_) => "circular_repeat",
Self::None => "none", Self::None => "none",
} }
} }
/// Dispatches interaction state updates to the corresponding shape-specific handler. /// 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, ctx: &mut GizmoContext) {
match self { match self {
Self::Star(h) => h.handle_state(layer, mouse_position, document, responses), Self::Star(h) => h.handle_state(layer, mouse_position, ctx),
Self::Polygon(h) => h.handle_state(layer, mouse_position, document, responses), Self::Polygon(h) => h.handle_state(layer, mouse_position, ctx),
Self::Arc(h) => h.handle_state(layer, mouse_position, document, responses), Self::Arc(h) => h.handle_state(layer, mouse_position, ctx),
Self::Circle(h) => h.handle_state(layer, mouse_position, document, responses), Self::Circle(h) => h.handle_state(layer, mouse_position, ctx),
Self::CircularRepeat(h) => h.handle_state(layer, mouse_position, ctx),
Self::None => {} Self::None => {}
} }
} }
@@ -61,6 +63,7 @@ impl ShapeGizmoHandlers {
Self::Polygon(h) => h.is_any_gizmo_hovered(), Self::Polygon(h) => h.is_any_gizmo_hovered(),
Self::Arc(h) => h.is_any_gizmo_hovered(), Self::Arc(h) => h.is_any_gizmo_hovered(),
Self::Circle(h) => h.is_any_gizmo_hovered(), Self::Circle(h) => h.is_any_gizmo_hovered(),
Self::CircularRepeat(h) => h.is_any_gizmo_hovered(),
Self::None => false, Self::None => false,
} }
} }
@@ -72,17 +75,19 @@ impl ShapeGizmoHandlers {
Self::Polygon(h) => h.handle_click(), Self::Polygon(h) => h.handle_click(),
Self::Arc(h) => h.handle_click(), Self::Arc(h) => h.handle_click(),
Self::Circle(h) => h.handle_click(), Self::Circle(h) => h.handle_click(),
Self::CircularRepeat(h) => h.handle_click(),
Self::None => {} Self::None => {}
} }
} }
/// Updates the gizmo state while the user is dragging a handle (e.g., adjusting radius). /// 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, ctx: &mut GizmoContext) {
match self { match self {
Self::Star(h) => h.handle_update(drag_start, document, input, responses), Self::Star(h) => h.handle_update(drag_start, ctx),
Self::Polygon(h) => h.handle_update(drag_start, document, input, responses), Self::Polygon(h) => h.handle_update(drag_start, ctx),
Self::Arc(h) => h.handle_update(drag_start, document, input, responses), Self::Arc(h) => h.handle_update(drag_start, ctx),
Self::Circle(h) => h.handle_update(drag_start, document, input, responses), Self::Circle(h) => h.handle_update(drag_start, ctx),
Self::CircularRepeat(h) => h.handle_update(drag_start, ctx),
Self::None => {} Self::None => {}
} }
} }
@@ -94,43 +99,31 @@ impl ShapeGizmoHandlers {
Self::Polygon(h) => h.cleanup(), Self::Polygon(h) => h.cleanup(),
Self::Arc(h) => h.cleanup(), Self::Arc(h) => h.cleanup(),
Self::Circle(h) => h.cleanup(), Self::Circle(h) => h.cleanup(),
Self::CircularRepeat(h) => h.cleanup(),
Self::None => {} Self::None => {}
} }
} }
/// Draws overlays like control points or outlines for the shape handled by this gizmo. /// Draws overlays like control points or outlines for the shape handled by this gizmo.
pub fn overlays( pub fn overlays(&self, layer: Option<LayerNodeIdentifier>, mouse_position: DVec2, ctx: &mut GizmoContext, overlay_context: &mut OverlayContext) {
&self,
document: &DocumentMessageHandler,
layer: Option<LayerNodeIdentifier>,
input: &InputPreprocessorMessageHandler,
shape_editor: &mut &mut ShapeState,
mouse_position: DVec2,
overlay_context: &mut OverlayContext,
) {
match self { match self {
Self::Star(h) => h.overlays(document, layer, input, shape_editor, mouse_position, overlay_context), Self::Star(h) => h.overlays(layer, mouse_position, ctx, overlay_context),
Self::Polygon(h) => h.overlays(document, layer, input, shape_editor, mouse_position, overlay_context), Self::Polygon(h) => h.overlays(layer, mouse_position, ctx, overlay_context),
Self::Arc(h) => h.overlays(document, layer, input, shape_editor, mouse_position, overlay_context), Self::Arc(h) => h.overlays(layer, mouse_position, ctx, overlay_context),
Self::Circle(h) => h.overlays(document, layer, input, shape_editor, mouse_position, overlay_context), Self::Circle(h) => h.overlays(layer, mouse_position, ctx, overlay_context),
Self::CircularRepeat(h) => h.overlays(layer, mouse_position, ctx, overlay_context),
Self::None => {} Self::None => {}
} }
} }
/// Draws live-updating overlays during drag interactions for the shape handled by this gizmo. /// Draws live-updating overlays during drag interactions for the shape handled by this gizmo.
pub fn dragging_overlays( pub fn dragging_overlays(&self, mouse_position: DVec2, ctx: &mut GizmoContext, overlay_context: &mut OverlayContext) {
&self,
document: &DocumentMessageHandler,
input: &InputPreprocessorMessageHandler,
shape_editor: &mut &mut ShapeState,
mouse_position: DVec2,
overlay_context: &mut OverlayContext,
) {
match self { match self {
Self::Star(h) => h.dragging_overlays(document, input, shape_editor, mouse_position, overlay_context), Self::Star(h) => h.dragging_overlays(mouse_position, ctx, overlay_context),
Self::Polygon(h) => h.dragging_overlays(document, input, shape_editor, mouse_position, overlay_context), Self::Polygon(h) => h.dragging_overlays(mouse_position, ctx, overlay_context),
Self::Arc(h) => h.dragging_overlays(document, input, shape_editor, mouse_position, overlay_context), Self::Arc(h) => h.dragging_overlays(mouse_position, ctx, overlay_context),
Self::Circle(h) => h.dragging_overlays(document, input, shape_editor, mouse_position, overlay_context), Self::Circle(h) => h.dragging_overlays(mouse_position, ctx, overlay_context),
Self::CircularRepeat(h) => h.dragging_overlays(mouse_position, ctx, overlay_context),
Self::None => {} Self::None => {}
} }
} }
@@ -141,6 +134,7 @@ impl ShapeGizmoHandlers {
Self::Polygon(h) => h.mouse_cursor_icon(), Self::Polygon(h) => h.mouse_cursor_icon(),
Self::Arc(h) => h.mouse_cursor_icon(), Self::Arc(h) => h.mouse_cursor_icon(),
Self::Circle(h) => h.mouse_cursor_icon(), Self::Circle(h) => h.mouse_cursor_icon(),
Self::CircularRepeat(h) => h.mouse_cursor_icon(),
Self::None => None, Self::None => None,
} }
} }
@@ -188,6 +182,16 @@ impl GizmoManager {
None None
} }
/// Detects and returns a operation gizmo handler based on the layer type (e.g., circular_repeat, repeat).
pub fn detect_operation_gizmo_handler(layer: LayerNodeIdentifier, document: &DocumentMessageHandler) -> Option<ShapeGizmoHandlers> {
// Circular Repeat
if graph_modification_utils::get_circular_repeat(layer, &document.network_interface).is_some() {
return Some(ShapeGizmoHandlers::CircularRepeat(CircularRepeatGizmoHandler::default()));
}
None
}
/// Returns `true` if a gizmo is currently active (hovered or being interacted with). /// Returns `true` if a gizmo is currently active (hovered or being interacted with).
pub fn hovering_over_gizmo(&self) -> bool { pub fn hovering_over_gizmo(&self) -> bool {
self.active_shape_handler.is_some() self.active_shape_handler.is_some()
@@ -196,12 +200,12 @@ impl GizmoManager {
/// Called every frame to check selected layers and update the active shape gizmo, if hovered. /// 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. /// 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, ctx: &mut GizmoContext) {
let mut handlers_layer: Vec<(ShapeGizmoHandlers, Vec<LayerNodeIdentifier>)> = Vec::new(); 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) { for layer in ctx.document.network_interface.selected_nodes().selected_visible_and_unlocked_layers(&ctx.document.network_interface) {
if let Some(mut handler) = Self::detect_shape_handler(layer, document) { if let Some(mut handler) = Self::detect_shape_handler(layer, ctx.document) {
handler.handle_state(layer, mouse_position, document, responses); handler.handle_state(layer, mouse_position, ctx);
let is_hovered = handler.is_any_gizmo_hovered(); let is_hovered = handler.is_any_gizmo_hovered();
if is_hovered { if is_hovered {
@@ -210,7 +214,7 @@ impl GizmoManager {
return; return;
} }
// Try to group this handler with others of the same type // Group same-kind handlers together
if let Some((_, layers)) = handlers_layer.iter_mut().find(|(existing_handler, _)| existing_handler.kind() == handler.kind()) { if let Some((_, layers)) = handlers_layer.iter_mut().find(|(existing_handler, _)| existing_handler.kind() == handler.kind()) {
layers.push(layer); layers.push(layer);
} else { } else {
@@ -223,6 +227,38 @@ impl GizmoManager {
self.active_shape_handler = None; self.active_shape_handler = None;
} }
pub fn handle_operation_actions(&mut self, mouse_position: DVec2, ctx: &mut GizmoContext) {
self.active_shape_handler = None;
let mut handlers_map: HashMap<&'static str, ShapeGizmoHandlers> = HashMap::new();
let mut maybe_active_kind: Option<&'static str> = None;
for layer in ctx.document.network_interface.selected_nodes().selected_visible_and_unlocked_layers(&ctx.document.network_interface) {
if let Some(mut handler) = Self::detect_operation_gizmo_handler(layer, ctx.document) {
let kind = handler.kind();
// Reuse existing handler to accumulate layers
if let Some(existing_handler) = handlers_map.remove(kind) {
handler = existing_handler;
}
handler.handle_state(layer, mouse_position, ctx);
if handler.is_any_gizmo_hovered() {
maybe_active_kind = Some(kind);
}
handlers_map.insert(kind, handler);
}
}
if let Some(kind) = maybe_active_kind {
if let Some(handler) = handlers_map.remove(kind) {
self.active_shape_handler = Some(handler);
}
}
}
/// Handles click interactions if a gizmo is active. Returns `true` if a gizmo handled the click. /// Handles click interactions if a gizmo is active. Returns `true` if a gizmo handled the click.
pub fn handle_click(&mut self) -> bool { pub fn handle_click(&mut self) -> bool {
if let Some(handle) = &mut self.active_shape_handler { if let Some(handle) = &mut self.active_shape_handler {
@@ -239,45 +275,31 @@ impl GizmoManager {
} }
/// Passes drag update data to the active gizmo to update shape parameters live. /// 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, ctx: &mut GizmoContext) {
if let Some(handle) = &mut self.active_shape_handler { if let Some(handle) = &mut self.active_shape_handler {
handle.handle_update(drag_start, document, input, responses); handle.handle_update(drag_start, ctx);
} }
} }
/// Draws overlays for the currently active shape gizmo during a drag interaction. /// Draws overlays for the currently active shape gizmo during a drag interaction.
pub fn dragging_overlays( pub fn dragging_overlays(&self, mouse_position: DVec2, ctx: &mut GizmoContext, overlay_context: &mut OverlayContext) {
&self,
document: &DocumentMessageHandler,
input: &InputPreprocessorMessageHandler,
shape_editor: &mut &mut ShapeState,
mouse_position: DVec2,
overlay_context: &mut OverlayContext,
) {
if let Some(handle) = &self.active_shape_handler { if let Some(handle) = &self.active_shape_handler {
handle.dragging_overlays(document, input, shape_editor, mouse_position, overlay_context); handle.dragging_overlays(mouse_position, ctx, overlay_context);
} }
} }
/// Draws overlays for either the active gizmo (if hovered) or all grouped selected gizmos. /// Draws overlays for either the active gizmo (if hovered) or all grouped selected gizmos.
/// ///
/// If no single gizmo is active, it renders overlays for all grouped layers with associated handlers. /// If no single gizmo is active, it renders overlays for all grouped layers with associated handlers.
pub fn overlays( pub fn overlays(&self, mouse_position: DVec2, ctx: &mut GizmoContext, overlay_context: &mut OverlayContext) {
&self,
document: &DocumentMessageHandler,
input: &InputPreprocessorMessageHandler,
shape_editor: &mut &mut ShapeState,
mouse_position: DVec2,
overlay_context: &mut OverlayContext,
) {
if let Some(handler) = &self.active_shape_handler { if let Some(handler) = &self.active_shape_handler {
handler.overlays(document, None, input, shape_editor, mouse_position, overlay_context); handler.overlays(None, mouse_position, ctx, overlay_context);
return; return;
} }
for (handler, selected_layers) in &self.layers_handlers { for (handler, selected_layers) in &self.layers_handlers {
for layer in selected_layers { for layer in selected_layers {
handler.overlays(document, Some(*layer), input, shape_editor, mouse_position, overlay_context); handler.overlays(Some(*layer), mouse_position, ctx, overlay_context);
} }
} }
} }
@@ -1,2 +1,3 @@
pub mod gizmo_manager; pub mod gizmo_manager;
pub mod operation_gizmos;
pub mod shape_gizmos; pub mod shape_gizmos;
@@ -0,0 +1,156 @@
use crate::consts::{NUMBER_OF_POINTS_DIAL_SPOKE_EXTENSION, NUMBER_OF_POINTS_DIAL_SPOKE_LENGTH};
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::NodeGraphMessage;
use crate::messages::prelude::Responses;
use crate::messages::tool::common_functionality::graph_modification_utils;
use crate::messages::tool::common_functionality::shapes::shape_utility::{GizmoContext, extract_circular_repeat_parameters};
use glam::{DAffine2, DVec2};
use graph_craft::document::NodeInput;
use graph_craft::document::value::TaggedValue;
use std::collections::HashMap;
use std::f64::consts::{FRAC_PI_2, TAU};
#[derive(Clone, Debug, Default, PartialEq)]
pub enum RepeatCountDialState {
#[default]
Inactive,
Hover,
Dragging,
}
#[derive(Clone, Debug, Default)]
pub struct RepeatCountDial {
pub layer: Option<LayerNodeIdentifier>,
pub initial_points: u32,
pub handle_state: RepeatCountDialState,
// store the other layers whose gizmo is not hovered but still has repeat node in them
selected_layers: HashMap<LayerNodeIdentifier, u32>,
}
impl RepeatCountDial {
pub fn cleanup(&mut self) {
self.handle_state = RepeatCountDialState::Inactive;
self.layer = None;
self.selected_layers.clear();
}
pub fn update_state(&mut self, state: RepeatCountDialState) {
self.handle_state = state;
}
pub fn is_hovering(&self) -> bool {
self.handle_state == RepeatCountDialState::Hover
}
pub fn is_dragging(&self) -> bool {
self.handle_state == RepeatCountDialState::Dragging
}
pub fn handle_actions(&mut self, layer: LayerNodeIdentifier, mouse_position: DVec2, ctx: &mut GizmoContext) {
let GizmoContext { document, .. } = ctx;
match &self.handle_state {
RepeatCountDialState::Inactive => {
let Some((_, radius, count)) = extract_circular_repeat_parameters(Some(layer), document) else {
return;
};
let viewport = document.metadata().transform_to_viewport(layer);
let center = viewport.transform_point2(DVec2::ZERO);
let offset_vector = document.metadata().downstream_transform_to_viewport(layer).transform_vector2(DVec2::NEG_Y * radius);
let repeat_center = center + offset_vector;
if repeat_center.distance(mouse_position) < NUMBER_OF_POINTS_DIAL_SPOKE_LENGTH {
self.layer = Some(layer);
self.update_state(RepeatCountDialState::Hover);
self.initial_points = count;
}
self.selected_layers.insert(layer, count);
}
RepeatCountDialState::Hover | RepeatCountDialState::Dragging => {
// Even though we the gizmo is in hovered state store the other layers
let Some((_, _, count)) = extract_circular_repeat_parameters(Some(layer), document) else {
return;
};
self.selected_layers.insert(layer, count);
}
}
}
pub fn overlays(&self, layer: Option<LayerNodeIdentifier>, mouse_position: DVec2, ctx: &mut GizmoContext, overlay_context: &mut OverlayContext) {
let GizmoContext { document, .. } = ctx;
match &self.handle_state {
RepeatCountDialState::Inactive => {
let Some(layer) = layer else { return };
let Some((angle, radius, count)) = extract_circular_repeat_parameters(Some(layer), document) else {
return;
};
let viewport = document.metadata().transform_to_viewport(layer);
let center = viewport.transform_point2(DVec2::ZERO);
let offset_vector = document.metadata().downstream_transform_to_viewport(layer).transform_vector2(DVec2::NEG_Y * radius);
let repeat_center = center + offset_vector;
if repeat_center.distance(mouse_position) < radius.abs() {
self.draw_spokes(repeat_center, document.metadata().downstream_transform_to_viewport(layer), count, angle.to_radians(), overlay_context);
}
}
RepeatCountDialState::Hover | RepeatCountDialState::Dragging => {
let Some(layer) = self.layer else { return };
let Some((angle, radius, count)) = extract_circular_repeat_parameters(Some(layer), document) else {
return;
};
let viewport = document.metadata().transform_to_viewport(layer);
let center = viewport.transform_point2(DVec2::ZERO);
let offset_vector = document.metadata().downstream_transform_to_viewport(layer).transform_vector2(DVec2::NEG_Y * radius);
let repeat_center = center + offset_vector;
self.draw_spokes(repeat_center, document.metadata().downstream_transform_to_viewport(layer), count, angle.to_radians(), overlay_context);
}
}
}
fn draw_spokes(&self, center: DVec2, viewport: DAffine2, count: u32, angle: f64, overlay_context: &mut OverlayContext) {
for i in 0..count {
let angle = ((i as f64) * TAU) / (count as f64) + angle + FRAC_PI_2;
let direction_vector = viewport.transform_vector2(DVec2 { x: angle.cos(), y: -angle.sin() });
let end_point = direction_vector * 20.;
if matches!(self.handle_state, RepeatCountDialState::Hover | RepeatCountDialState::Dragging) {
overlay_context.line(center, end_point * NUMBER_OF_POINTS_DIAL_SPOKE_EXTENSION + center, None, None);
} else {
overlay_context.line(center, end_point + center, None, None);
}
}
}
pub fn update_number_of_sides(&self, drag_start: DVec2, ctx: &mut GizmoContext) {
let GizmoContext { document, input, responses, .. } = ctx;
let delta = input.mouse.position - drag_start;
let sign = (input.mouse.position.x - drag_start.x).signum();
let net_delta = (delta.length() / 25.).round() * sign;
for (layer, count) in &self.selected_layers {
let Some(node_id) = graph_modification_utils::get_circular_repeat(*layer, &document.network_interface) else {
return;
};
let new_point_count = ((*count as i32) + (net_delta as i32)).max(1);
responses.add(NodeGraphMessage::SetInput {
input_connector: InputConnector::node(node_id, 3),
input: NodeInput::value(TaggedValue::U32(new_point_count as u32), false),
});
}
responses.add(NodeGraphMessage::RunDocumentGraph);
}
}
@@ -0,0 +1 @@
pub mod count_gizmos;
@@ -1,17 +1,15 @@
use crate::consts::GIZMO_HIDE_THRESHOLD; use crate::consts::GIZMO_HIDE_THRESHOLD;
use crate::messages::frontend::utility_types::MouseCursorIcon; 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::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::portfolio::document::utility_types::network_interface::InputConnector;
use crate::messages::prelude::{DocumentMessageHandler, InputPreprocessorMessageHandler, NodeGraphMessage}; use crate::messages::prelude::{DocumentMessageHandler, NodeGraphMessage};
use crate::messages::prelude::{FrontendMessage, Responses}; 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::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 crate::messages::tool::common_functionality::shapes::shape_utility::{GizmoContext, extract_arc_parameters, extract_circle_radius};
use glam::{DAffine2, DVec2}; use glam::{DAffine2, DVec2};
use graph_craft::document::NodeInput; use graph_craft::document::NodeInput;
use graph_craft::document::value::TaggedValue; use graph_craft::document::value::TaggedValue;
use std::collections::VecDeque;
use std::f64::consts::FRAC_PI_2; use std::f64::consts::FRAC_PI_2;
#[derive(Clone, Debug, Default, PartialEq)] #[derive(Clone, Debug, Default, PartialEq)]
@@ -77,7 +75,9 @@ impl RadiusHandle {
stroke_width + extra_spacing 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, mouse_position: DVec2, ctx: &mut GizmoContext) {
let GizmoContext { document, responses, .. } = ctx;
match &self.handle_state { match &self.handle_state {
RadiusHandleState::Inactive => { RadiusHandleState::Inactive => {
let Some(radius) = extract_circle_radius(layer, document).or(extract_arc_parameters(Some(layer), document).map(|(r, _, _, _)| r)) else { let Some(radius) = extract_circle_radius(layer, document).or(extract_arc_parameters(Some(layer), document).map(|(r, _, _, _)| r)) else {
@@ -107,7 +107,9 @@ impl RadiusHandle {
} }
} }
pub fn overlays(&self, document: &DocumentMessageHandler, overlay_context: &mut OverlayContext) { pub fn overlays(&self, ctx: &mut GizmoContext, overlay_context: &mut OverlayContext) {
let GizmoContext { document, .. } = ctx;
match &self.handle_state { match &self.handle_state {
RadiusHandleState::Inactive => {} RadiusHandleState::Inactive => {}
RadiusHandleState::Dragging | RadiusHandleState::Hover => { RadiusHandleState::Dragging | RadiusHandleState::Hover => {
@@ -145,7 +147,9 @@ impl RadiusHandle {
} }
} }
pub fn update_inner_radius(&mut self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>, drag_start: DVec2) { pub fn update_inner_radius(&mut self, drag_start: DVec2, ctx: &mut GizmoContext) {
let GizmoContext { document, responses, input, .. } = ctx;
let Some(layer) = self.layer else { return }; let Some(layer) = self.layer else { return };
let Some(node_id) = graph_modification_utils::get_circle_id(layer, &document.network_interface).or(get_arc_id(layer, &document.network_interface)) else { let Some(node_id) = graph_modification_utils::get_circle_id(layer, &document.network_interface).or(get_arc_id(layer, &document.network_interface)) else {
return; return;
@@ -1,19 +1,18 @@
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::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::frontend::utility_types::MouseCursorIcon;
use crate::messages::message::Message;
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::portfolio::document::utility_types::network_interface::InputConnector;
use crate::messages::prelude::Responses; use crate::messages::prelude::Responses;
use crate::messages::prelude::{DocumentMessageHandler, FrontendMessage, InputPreprocessorMessageHandler, NodeGraphMessage}; use crate::messages::prelude::{FrontendMessage, NodeGraphMessage};
use crate::messages::tool::common_functionality::graph_modification_utils; 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::{
use crate::messages::tool::common_functionality::shapes::shape_utility::{extract_polygon_parameters, inside_polygon, inside_star, polygon_outline, polygon_vertex_position, star_outline}; GizmoContext, extract_polygon_parameters, inside_polygon, inside_star, polygon_outline, polygon_vertex_position, star_outline,
};
use crate::messages::tool::common_functionality::shapes::shape_utility::{extract_star_parameters, star_vertex_position}; use crate::messages::tool::common_functionality::shapes::shape_utility::{extract_star_parameters, star_vertex_position};
use glam::{DAffine2, DVec2}; use glam::{DAffine2, DVec2};
use graph_craft::document::NodeInput; use graph_craft::document::NodeInput;
use graph_craft::document::value::TaggedValue; use graph_craft::document::value::TaggedValue;
use std::collections::VecDeque;
use std::f64::consts::TAU; use std::f64::consts::TAU;
#[derive(Clone, Debug, Default, PartialEq)] #[derive(Clone, Debug, Default, PartialEq)]
@@ -49,7 +48,8 @@ impl NumberOfPointsDial {
self.handle_state == NumberOfPointsDialState::Dragging 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, ctx: &mut GizmoContext) {
let GizmoContext { document, responses, .. } = ctx;
match &self.handle_state { match &self.handle_state {
NumberOfPointsDialState::Inactive => { NumberOfPointsDialState::Inactive => {
// Star // Star
@@ -97,7 +97,9 @@ impl NumberOfPointsDial {
} }
} }
pub fn overlays(&self, document: &DocumentMessageHandler, layer: Option<LayerNodeIdentifier>, shape_editor: &mut &mut ShapeState, mouse_position: DVec2, overlay_context: &mut OverlayContext) { pub fn overlays(&self, layer: Option<LayerNodeIdentifier>, mouse_position: DVec2, ctx: &mut GizmoContext, overlay_context: &mut OverlayContext) {
let GizmoContext { document, shape_editor, .. } = ctx;
match &self.handle_state { match &self.handle_state {
NumberOfPointsDialState::Inactive => { NumberOfPointsDialState::Inactive => {
let Some(layer) = layer else { return }; let Some(layer) = layer else { return };
@@ -188,7 +190,9 @@ impl NumberOfPointsDial {
} }
} }
pub fn update_number_of_sides(&self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>, drag_start: DVec2) { pub fn update_number_of_sides(&self, drag_start: DVec2, ctx: &mut GizmoContext) {
let GizmoContext { document, input, responses, .. } = ctx;
let delta = input.mouse.position - drag_start; let delta = input.mouse.position - drag_start;
let sign = (input.mouse.position.x - drag_start.x).signum(); let sign = (input.mouse.position.x - drag_start.x).signum();
let net_delta = (delta.length() / 25.).round() * sign; let net_delta = (delta.length() / 25.).round() * sign;
@@ -1,19 +1,17 @@
use crate::consts::GIZMO_HIDE_THRESHOLD; use crate::consts::GIZMO_HIDE_THRESHOLD;
use crate::consts::{COLOR_OVERLAY_RED, POINT_RADIUS_HANDLE_SNAP_THRESHOLD}; use crate::consts::{COLOR_OVERLAY_RED, POINT_RADIUS_HANDLE_SNAP_THRESHOLD};
use crate::messages::frontend::utility_types::MouseCursorIcon; 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::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::{overlays::utility_types::OverlayContext, utility_types::network_interface::InputConnector}; use crate::messages::portfolio::document::{overlays::utility_types::OverlayContext, utility_types::network_interface::InputConnector};
use crate::messages::prelude::FrontendMessage; use crate::messages::prelude::FrontendMessage;
use crate::messages::prelude::Responses; use crate::messages::prelude::Responses;
use crate::messages::prelude::{DocumentMessageHandler, InputPreprocessorMessageHandler, NodeGraphMessage}; use crate::messages::prelude::{DocumentMessageHandler, NodeGraphMessage};
use crate::messages::tool::common_functionality::graph_modification_utils::{self, NodeGraphLayer}; use crate::messages::tool::common_functionality::graph_modification_utils::{self, NodeGraphLayer};
use crate::messages::tool::common_functionality::shapes::shape_utility::{draw_snapping_ticks, extract_polygon_parameters, polygon_outline, polygon_vertex_position, star_outline}; use crate::messages::tool::common_functionality::shapes::shape_utility::{GizmoContext, draw_snapping_ticks, extract_polygon_parameters, polygon_outline, polygon_vertex_position, star_outline};
use crate::messages::tool::common_functionality::shapes::shape_utility::{extract_star_parameters, star_vertex_position}; use crate::messages::tool::common_functionality::shapes::shape_utility::{extract_star_parameters, star_vertex_position};
use glam::DVec2; use glam::DVec2;
use graph_craft::document::NodeInput; use graph_craft::document::NodeInput;
use graph_craft::document::value::TaggedValue; use graph_craft::document::value::TaggedValue;
use std::collections::VecDeque;
use std::f64::consts::{FRAC_1_SQRT_2, FRAC_PI_4, PI, SQRT_2}; use std::f64::consts::{FRAC_1_SQRT_2, FRAC_PI_4, PI, SQRT_2};
#[derive(Clone, Debug, Default, PartialEq)] #[derive(Clone, Debug, Default, PartialEq)]
@@ -54,7 +52,8 @@ impl PointRadiusHandle {
self.handle_state = state; 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, mouse_position: DVec2, ctx: &mut GizmoContext) {
let GizmoContext { document, responses, .. } = ctx;
match &self.handle_state { match &self.handle_state {
PointRadiusHandleState::Inactive => { PointRadiusHandleState::Inactive => {
// Draw the point handle gizmo for the star shape // Draw the point handle gizmo for the star shape
@@ -142,7 +141,9 @@ impl PointRadiusHandle {
} }
} }
pub fn overlays(&self, selected_star_layer: Option<LayerNodeIdentifier>, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, overlay_context: &mut OverlayContext) { pub fn overlays(&self, selected_star_layer: Option<LayerNodeIdentifier>, ctx: &mut GizmoContext, overlay_context: &mut OverlayContext) {
let GizmoContext { document, input, .. } = ctx;
match &self.handle_state { match &self.handle_state {
PointRadiusHandleState::Inactive => { PointRadiusHandleState::Inactive => {
let Some(layer) = selected_star_layer else { return }; let Some(layer) = selected_star_layer else { return };
@@ -411,7 +412,9 @@ impl PointRadiusHandle {
.map(|(i, rad)| (i, *rad - original_radius)) .map(|(i, rad)| (i, *rad - original_radius))
} }
pub fn update_inner_radius(&mut self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>, drag_start: DVec2) { pub fn update_inner_radius(&mut self, drag_start: DVec2, ctx: &mut GizmoContext) {
let GizmoContext { document, responses, input, .. } = ctx;
let Some(layer) = self.layer else { return }; let Some(layer) = self.layer else { return };
let Some(node_id) = graph_modification_utils::get_star_id(layer, &document.network_interface).or(graph_modification_utils::get_polygon_id(layer, &document.network_interface)) else { let Some(node_id) = graph_modification_utils::get_star_id(layer, &document.network_interface).or(graph_modification_utils::get_polygon_id(layer, &document.network_interface)) else {
@@ -3,9 +3,8 @@ use crate::messages::message::Message;
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::portfolio::document::utility_types::network_interface::InputConnector;
use crate::messages::prelude::DocumentMessageHandler;
use crate::messages::tool::common_functionality::graph_modification_utils; use crate::messages::tool::common_functionality::graph_modification_utils;
use crate::messages::tool::common_functionality::shapes::shape_utility::{arc_end_points, calculate_arc_text_transform, extract_arc_parameters, format_rounded}; use crate::messages::tool::common_functionality::shapes::shape_utility::{GizmoContext, arc_end_points, calculate_arc_text_transform, extract_arc_parameters, format_rounded};
use crate::messages::tool::tool_messages::tool_prelude::*; use crate::messages::tool::tool_messages::tool_prelude::*;
use glam::DVec2; use glam::DVec2;
use graph_craft::document::value::TaggedValue; use graph_craft::document::value::TaggedValue;
@@ -56,7 +55,9 @@ impl SweepAngleGizmo {
self.handle_state == SweepAngleGizmoState::Dragging || self.handle_state == SweepAngleGizmoState::Snapped self.handle_state == SweepAngleGizmoState::Dragging || self.handle_state == SweepAngleGizmoState::Snapped
} }
pub fn handle_actions(&mut self, layer: LayerNodeIdentifier, document: &DocumentMessageHandler, mouse_position: DVec2) { pub fn handle_actions(&mut self, layer: LayerNodeIdentifier, mouse_position: DVec2, ctx: &mut GizmoContext) {
let GizmoContext { document, .. } = ctx;
if self.handle_state == SweepAngleGizmoState::Inactive { if self.handle_state == SweepAngleGizmoState::Inactive {
let Some((start, end)) = arc_end_points(Some(layer), document) else { return }; let Some((start, end)) = arc_end_points(Some(layer), document) else { return };
let Some((_, start_angle, sweep_angle, _)) = extract_arc_parameters(Some(layer), document) else { let Some((_, start_angle, sweep_angle, _)) = extract_arc_parameters(Some(layer), document) else {
@@ -92,14 +93,9 @@ impl SweepAngleGizmo {
} }
} }
pub fn overlays( pub fn overlays(&self, selected_arc_layer: Option<LayerNodeIdentifier>, _mouse_position: DVec2, ctx: &mut GizmoContext, overlay_context: &mut OverlayContext) {
&self, let GizmoContext { document, .. } = ctx;
selected_arc_layer: Option<LayerNodeIdentifier>,
document: &DocumentMessageHandler,
_input: &InputPreprocessorMessageHandler,
_mouse_position: DVec2,
overlay_context: &mut OverlayContext,
) {
let tilt_offset = document.document_ptz.unmodified_tilt(); let tilt_offset = document.document_ptz.unmodified_tilt();
match self.handle_state { match self.handle_state {
@@ -183,7 +179,9 @@ impl SweepAngleGizmo {
overlay_context.arc_sweep_angle(offset_angle, angle, final_point, bold_radius, center, &text, transform); overlay_context.arc_sweep_angle(offset_angle, angle, final_point, bold_radius, center, &text, transform);
} }
pub fn update_arc(&mut self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) { pub fn update_arc(&mut self, ctx: &mut GizmoContext) {
let GizmoContext { document, input, responses, .. } = ctx;
let Some(layer) = self.layer else { return }; let Some(layer) = self.layer else { return };
let Some((_, current_start_angle, current_sweep_angle, _)) = extract_arc_parameters(Some(layer), document) else { let Some((_, current_start_angle, current_sweep_angle, _)) = extract_arc_parameters(Some(layer), document) else {
return; return;
@@ -367,6 +367,10 @@ pub fn get_text_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkIn
NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name("Text") NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name("Text")
} }
pub fn get_circular_repeat(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<NodeId> {
NodeGraphLayer::new(layer, network_interface).upstream_node_id_from_name("Circular Repeat")
}
/// Gets properties from the Text node /// Gets properties from the Text node
pub fn get_text(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<(&String, &Font, TypesettingConfig, bool)> { 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")?; let inputs = NodeGraphLayer::new(layer, network_interface).find_node_inputs("Text")?;
@@ -1,10 +1,60 @@
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::tool::common_functionality::shapes::shape_utility::extract_circular_repeat_parameters; use crate::messages::tool::common_functionality::gizmos::operation_gizmos::count_gizmos::{RepeatCountDial, RepeatCountDialState};
use crate::messages::tool::common_functionality::shapes::shape_utility::{GizmoContext, ShapeGizmoHandler, extract_circular_repeat_parameters};
use crate::messages::tool::tool_messages::operation_tool::{OperationToolData, OperationToolFsmState}; use crate::messages::tool::tool_messages::operation_tool::{OperationToolData, OperationToolFsmState};
use crate::messages::tool::tool_messages::tool_prelude::*; use crate::messages::tool::tool_messages::tool_prelude::*;
use std::collections::VecDeque; use std::collections::VecDeque;
#[derive(Clone, Debug, Default)]
pub struct CircularRepeatGizmoHandler {
count_dial: RepeatCountDial,
}
impl CircularRepeatGizmoHandler {
pub fn new() -> Self {
Self { ..Default::default() }
}
}
impl ShapeGizmoHandler for CircularRepeatGizmoHandler {
fn handle_state(&mut self, selected_shape_layer: LayerNodeIdentifier, mouse_position: DVec2, ctx: &mut GizmoContext) {
self.count_dial.handle_actions(selected_shape_layer, mouse_position, ctx);
}
fn is_any_gizmo_hovered(&self) -> bool {
self.count_dial.is_hovering()
}
fn handle_click(&mut self) {
if self.count_dial.is_hovering() {
self.count_dial.update_state(RepeatCountDialState::Dragging);
}
}
fn handle_update(&mut self, drag_start: DVec2, ctx: &mut GizmoContext) {
if self.count_dial.is_dragging() {
self.count_dial.update_number_of_sides(drag_start, ctx);
}
}
fn dragging_overlays(&self, mouse_position: DVec2, ctx: &mut GizmoContext, overlay_context: &mut OverlayContext) {
if self.count_dial.is_dragging() {
self.count_dial.overlays(None, mouse_position, ctx, overlay_context);
}
}
fn overlays(&self, selected_shape_layer: Option<LayerNodeIdentifier>, mouse_position: DVec2, ctx: &mut GizmoContext, overlay_context: &mut OverlayContext) {
self.count_dial.overlays(selected_shape_layer, mouse_position, ctx, overlay_context);
}
fn mouse_cursor_icon(&self) -> Option<MouseCursorIcon> {
None
}
fn cleanup(&mut self) {}
}
#[derive(Default)] #[derive(Default)]
pub struct CircularRepeatOperation; pub struct CircularRepeatOperation;
@@ -77,9 +127,7 @@ impl CircularRepeatOperation {
for (layer, initial_radius) in &tool_data.circular_operation_data.layers_dragging { for (layer, initial_radius) in &tool_data.circular_operation_data.layers_dragging {
// If the layer’s sign differs from the clicked layer, invert delta to preserve consistent in/out dragging behavior // If the layer’s sign differs from the clicked layer, invert delta to preserve consistent in/out dragging behavior
let Some((angle, _, count)) = extract_circular_repeat_parameters(Some(*layer), document) else { let (angle, _, count) = extract_circular_repeat_parameters(Some(*layer), document).unwrap_or((0.0, 0.0, 6));
return;
};
let new_radius = if initial_radius.signum() == clicked_radius.signum() { let new_radius = if initial_radius.signum() == clicked_radius.signum() {
*initial_radius + delta *initial_radius + delta
@@ -107,25 +155,12 @@ impl CircularRepeatOperation {
) { ) {
match tool_state { match tool_state {
OperationToolFsmState::Ready => { OperationToolFsmState::Ready => {
// Draw overlays for all selected layers
for layer in document.network_interface.selected_nodes().selected_layers(document.metadata()) {
Self::draw_layer_overlay(layer, document, input, overlay_context)
}
// Also highlight the hovered layer if it’s not selected // Also highlight the hovered layer if it’s not selected
if let Some(layer) = document.click(&input) { if let Some(layer) = document.click(&input) {
Self::draw_layer_overlay(layer, document, input, overlay_context); Self::draw_layer_overlay(layer, document, input, overlay_context);
} }
} }
_ => { _ => {}
// While dragging, only draw overlays for the layers being modified
for layer in tool_data.circular_operation_data.layers_dragging.iter().map(|(l, _)| l) {
let Some(vector) = document.network_interface.compute_modified_vector(*layer) else { continue };
let viewport = document.metadata().transform_to_viewport(*layer);
overlay_context.outline_vector(&vector, viewport);
}
}
} }
} }
@@ -133,11 +168,11 @@ impl CircularRepeatOperation {
if let Some(vector) = document.network_interface.compute_modified_vector(layer) { if let Some(vector) = document.network_interface.compute_modified_vector(layer) {
let viewport = document.metadata().transform_to_viewport(layer); let viewport = document.metadata().transform_to_viewport(layer);
let center = viewport.transform_point2(DVec2::ZERO); let center = viewport.transform_point2(DVec2::ZERO);
// Show a small circle if the mouse is near the repeat center // Show a small circle if the mouse is near the repeat center
if center.distance(input.mouse.position) < 5. { if center.distance(input.mouse.position) < 5. {
overlay_context.circle(center, 3., None, None); overlay_context.circle(center, 3., None, None);
} }
overlay_context.outline_vector(&vector, viewport);
} }
} }
@@ -2,12 +2,13 @@ use super::shape_utility::ShapeToolModifierKey;
use super::*; use super::*;
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::node_graph::document_node_definitions::resolve_document_node_type; 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::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate}; use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate};
use crate::messages::tool::common_functionality::gizmos::shape_gizmos::circle_arc_radius_handle::{RadiusHandle, RadiusHandleState}; use crate::messages::tool::common_functionality::gizmos::shape_gizmos::circle_arc_radius_handle::{RadiusHandle, RadiusHandleState};
use crate::messages::tool::common_functionality::gizmos::shape_gizmos::sweep_angle_gizmo::{SweepAngleGizmo, SweepAngleGizmoState}; 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::graph_modification_utils;
use crate::messages::tool::common_functionality::shapes::shape_utility::{ShapeGizmoHandler, arc_outline}; use crate::messages::tool::common_functionality::shapes::shape_utility::{GizmoContext, ShapeGizmoHandler, arc_outline};
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;
@@ -28,9 +29,9 @@ impl ArcGizmoHandler {
} }
impl ShapeGizmoHandler for 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, ctx: &mut GizmoContext) {
self.sweep_angle_gizmo.handle_actions(selected_shape_layer, document, mouse_position); self.sweep_angle_gizmo.handle_actions(selected_shape_layer, mouse_position, ctx);
self.arc_radius_handle.handle_actions(selected_shape_layer, document, mouse_position, responses); self.arc_radius_handle.handle_actions(selected_shape_layer, mouse_position, ctx);
} }
fn is_any_gizmo_hovered(&self) -> bool { fn is_any_gizmo_hovered(&self) -> bool {
@@ -54,61 +55,46 @@ 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, ctx: &mut GizmoContext) {
if self.sweep_angle_gizmo.is_dragging_or_snapped() { if self.sweep_angle_gizmo.is_dragging_or_snapped() {
self.sweep_angle_gizmo.update_arc(document, input, responses); self.sweep_angle_gizmo.update_arc(ctx);
} }
if self.arc_radius_handle.is_dragging() { if self.arc_radius_handle.is_dragging() {
self.arc_radius_handle.update_inner_radius(document, input, responses, drag_start); self.arc_radius_handle.update_inner_radius(drag_start, ctx);
} }
} }
fn dragging_overlays( fn dragging_overlays(&self, mouse_position: DVec2, ctx: &mut GizmoContext, overlay_context: &mut OverlayContext) {
&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.sweep_angle_gizmo.is_dragging_or_snapped() { if self.sweep_angle_gizmo.is_dragging_or_snapped() {
self.sweep_angle_gizmo.overlays(None, document, input, mouse_position, overlay_context); self.sweep_angle_gizmo.overlays(None, mouse_position, ctx, overlay_context);
arc_outline(self.sweep_angle_gizmo.layer, document, overlay_context); arc_outline(self.sweep_angle_gizmo.layer, ctx.document, overlay_context);
} }
if self.arc_radius_handle.is_dragging() { if self.arc_radius_handle.is_dragging() {
self.sweep_angle_gizmo.overlays(self.arc_radius_handle.layer, document, input, mouse_position, overlay_context); self.sweep_angle_gizmo.overlays(self.arc_radius_handle.layer, mouse_position, ctx, overlay_context);
self.arc_radius_handle.overlays(document, overlay_context); self.arc_radius_handle.overlays(ctx, overlay_context);
} }
} }
fn overlays( fn overlays(&self, selected_shape_layer: Option<LayerNodeIdentifier>, mouse_position: DVec2, ctx: &mut GizmoContext, overlay_context: &mut OverlayContext) {
&self,
document: &DocumentMessageHandler,
selected_shape_layer: Option<LayerNodeIdentifier>,
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 hovering over both the gizmos give priority to sweep angle gizmo // If hovering over both the gizmos give priority to sweep angle gizmo
if self.sweep_angle_gizmo.hovered() && self.arc_radius_handle.hovered() { if self.sweep_angle_gizmo.hovered() && self.arc_radius_handle.hovered() {
self.sweep_angle_gizmo.overlays(selected_shape_layer, document, input, mouse_position, overlay_context); self.sweep_angle_gizmo.overlays(selected_shape_layer, mouse_position, ctx, overlay_context);
return; return;
} }
if self.arc_radius_handle.hovered() { if self.arc_radius_handle.hovered() {
let layer = self.arc_radius_handle.layer; let layer = self.arc_radius_handle.layer;
self.arc_radius_handle.overlays(document, overlay_context); self.arc_radius_handle.overlays(ctx, overlay_context);
self.sweep_angle_gizmo.overlays(layer, document, input, mouse_position, overlay_context); self.sweep_angle_gizmo.overlays(layer, mouse_position, ctx, overlay_context);
} }
self.sweep_angle_gizmo.overlays(selected_shape_layer, document, input, mouse_position, overlay_context); self.sweep_angle_gizmo.overlays(selected_shape_layer, mouse_position, ctx, overlay_context);
self.arc_radius_handle.overlays(document, overlay_context); self.arc_radius_handle.overlays(ctx, overlay_context);
arc_outline(selected_shape_layer.or(self.sweep_angle_gizmo.layer), document, overlay_context); arc_outline(selected_shape_layer.or(self.sweep_angle_gizmo.layer), ctx.document, overlay_context);
} }
fn mouse_cursor_icon(&self) -> Option<MouseCursorIcon> { fn mouse_cursor_icon(&self) -> Option<MouseCursorIcon> {
@@ -5,8 +5,7 @@ use crate::messages::portfolio::document::utility_types::document_metadata::Laye
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate}; use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeTemplate};
use crate::messages::tool::common_functionality::gizmos::shape_gizmos::circle_arc_radius_handle::{RadiusHandle, RadiusHandleState}; use crate::messages::tool::common_functionality::gizmos::shape_gizmos::circle_arc_radius_handle::{RadiusHandle, RadiusHandleState};
use crate::messages::tool::common_functionality::graph_modification_utils; 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::{GizmoContext, ShapeGizmoHandler, ShapeToolModifierKey};
use crate::messages::tool::common_functionality::shapes::shape_utility::{ShapeGizmoHandler, ShapeToolModifierKey};
use crate::messages::tool::tool_messages::shape_tool::ShapeToolData; use crate::messages::tool::tool_messages::shape_tool::ShapeToolData;
use crate::messages::tool::tool_messages::tool_prelude::*; use crate::messages::tool::tool_messages::tool_prelude::*;
use glam::DAffine2; use glam::DAffine2;
@@ -23,8 +22,8 @@ impl ShapeGizmoHandler for CircleGizmoHandler {
self.circle_radius_handle.hovered() self.circle_radius_handle.hovered()
} }
fn handle_state(&mut self, selected_circle_layer: LayerNodeIdentifier, mouse_position: DVec2, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) { fn handle_state(&mut self, selected_circle_layer: LayerNodeIdentifier, mouse_position: DVec2, ctx: &mut GizmoContext) {
self.circle_radius_handle.handle_actions(selected_circle_layer, document, mouse_position, responses); self.circle_radius_handle.handle_actions(selected_circle_layer, mouse_position, ctx);
} }
fn handle_click(&mut self) { fn handle_click(&mut self) {
@@ -33,34 +32,19 @@ 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, ctx: &mut GizmoContext) {
if self.circle_radius_handle.is_dragging() { if self.circle_radius_handle.is_dragging() {
self.circle_radius_handle.update_inner_radius(document, input, responses, drag_start); self.circle_radius_handle.update_inner_radius(drag_start, ctx);
} }
} }
fn overlays( fn overlays(&self, _selected_circle_layer: Option<LayerNodeIdentifier>, _mouse_position: DVec2, ctx: &mut GizmoContext, overlay_context: &mut OverlayContext) {
&self, self.circle_radius_handle.overlays(ctx, overlay_context);
document: &DocumentMessageHandler,
_selected_circle_layer: Option<LayerNodeIdentifier>,
_input: &InputPreprocessorMessageHandler,
_shape_editor: &mut &mut ShapeState,
_mouse_position: DVec2,
overlay_context: &mut OverlayContext,
) {
self.circle_radius_handle.overlays(document, overlay_context);
} }
fn dragging_overlays( fn dragging_overlays(&self, _mouse_position: DVec2, ctx: &mut GizmoContext, overlay_context: &mut OverlayContext) {
&self,
document: &DocumentMessageHandler,
_input: &InputPreprocessorMessageHandler,
_shape_editor: &mut &mut ShapeState,
_mouse_position: DVec2,
overlay_context: &mut OverlayContext,
) {
if self.circle_radius_handle.is_dragging() { if self.circle_radius_handle.is_dragging() {
self.circle_radius_handle.overlays(document, overlay_context); self.circle_radius_handle.overlays(ctx, overlay_context);
} }
} }
@@ -11,7 +11,7 @@ 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::shape_editor::ShapeState; use crate::messages::tool::common_functionality::shapes::shape_utility::GizmoContext;
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::tool_prelude::*; use crate::messages::tool::tool_messages::tool_prelude::*;
@@ -31,9 +31,9 @@ impl ShapeGizmoHandler for PolygonGizmoHandler {
self.number_of_points_dial.is_hovering() || self.point_radius_handle.hovered() 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>) { fn handle_state(&mut self, selected_polygon_layer: LayerNodeIdentifier, mouse_position: DVec2, ctx: &mut GizmoContext) {
self.number_of_points_dial.handle_actions(selected_star_layer, mouse_position, document, responses); self.number_of_points_dial.handle_actions(selected_polygon_layer, mouse_position, ctx);
self.point_radius_handle.handle_actions(selected_star_layer, document, mouse_position, responses); self.point_radius_handle.handle_actions(selected_polygon_layer, mouse_position, ctx);
} }
fn handle_click(&mut self) { fn handle_click(&mut self) {
@@ -47,45 +47,31 @@ 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, ctx: &mut GizmoContext) {
if self.number_of_points_dial.is_dragging() { if self.number_of_points_dial.is_dragging() {
self.number_of_points_dial.update_number_of_sides(document, input, responses, drag_start); self.number_of_points_dial.update_number_of_sides(drag_start, ctx);
} }
if self.point_radius_handle.is_dragging_or_snapped() { if self.point_radius_handle.is_dragging_or_snapped() {
self.point_radius_handle.update_inner_radius(document, input, responses, drag_start); self.point_radius_handle.update_inner_radius(drag_start, ctx);
} }
} }
fn overlays( fn overlays(&self, selected_polygon_layer: Option<LayerNodeIdentifier>, mouse_position: DVec2, ctx: &mut GizmoContext, overlay_context: &mut OverlayContext) {
&self, self.number_of_points_dial.overlays(selected_polygon_layer, mouse_position, ctx, overlay_context);
document: &DocumentMessageHandler, self.point_radius_handle.overlays(selected_polygon_layer, ctx, overlay_context);
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, overlay_context);
let GizmoContext { document, .. } = ctx;
polygon_outline(selected_polygon_layer, document, overlay_context); polygon_outline(selected_polygon_layer, document, overlay_context);
} }
fn dragging_overlays( fn dragging_overlays(&self, mouse_position: DVec2, ctx: &mut GizmoContext, overlay_context: &mut OverlayContext) {
&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() { if self.number_of_points_dial.is_dragging() {
self.number_of_points_dial.overlays(document, None, shape_editor, mouse_position, overlay_context); self.number_of_points_dial.overlays(None, mouse_position, ctx, overlay_context);
} }
if self.point_radius_handle.is_dragging_or_snapped() { if self.point_radius_handle.is_dragging_or_snapped() {
self.point_radius_handle.overlays(None, document, input, overlay_context); self.point_radius_handle.overlays(None, ctx, overlay_context);
} }
} }
@@ -79,13 +79,20 @@ impl ShapeType {
pub type ShapeToolModifierKey = [Key; 3]; pub type ShapeToolModifierKey = [Key; 3];
pub struct GizmoContext<'a> {
pub document: &'a DocumentMessageHandler,
pub input: &'a InputPreprocessorMessageHandler,
pub responses: &'a mut VecDeque<Message>,
pub shape_editor: &'a mut ShapeState,
}
/// The `ShapeGizmoHandler` trait defines the interactive behavior and overlay logic for shape-specific tools in the editor. /// The `ShapeGizmoHandler` trait defines the interactive behavior and overlay logic for shape-specific tools in the editor.
/// A gizmo is a visual handle or control point used to manipulate a shape's properties (e.g., number of sides, radius, angle). /// A gizmo is a visual handle or control point used to manipulate a shape's properties (e.g., number of sides, radius, angle).
pub trait ShapeGizmoHandler { pub trait ShapeGizmoHandler {
/// Called every frame to update the gizmo's interaction state based on the mouse position and selection. /// 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). /// 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, layer: LayerNodeIdentifier, mouse_position: DVec2, ctx: &mut GizmoContext);
/// Called when a mouse click occurs over the canvas and a gizmo handle is hovered. /// Called when a mouse click occurs over the canvas and a gizmo handle is hovered.
/// ///
@@ -96,32 +103,17 @@ pub trait ShapeGizmoHandler {
/// Called during a drag interaction to update the shape's parameters in real time. /// 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. /// 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, ctx: &mut GizmoContext);
/// Draws the static or hover-dependent overlays associated with the gizmo. /// Draws the static or hover-dependent overlays associated with the gizmo.
/// ///
/// These overlays include visual indicators like shape outlines, control points, and hover highlights. /// These overlays include visual indicators like shape outlines, control points, and hover highlights.
fn overlays( fn overlays(&self, selected_shape_layers: Option<LayerNodeIdentifier>, mouse_position: DVec2, ctx: &mut GizmoContext, overlay_context: &mut OverlayContext);
&self,
document: &DocumentMessageHandler,
selected_shape_layers: Option<LayerNodeIdentifier>,
input: &InputPreprocessorMessageHandler,
shape_editor: &mut &mut ShapeState,
mouse_position: DVec2,
overlay_context: &mut OverlayContext,
);
/// Draws overlays specifically during a drag operation. /// Draws overlays specifically during a drag operation.
/// ///
/// Used to give real-time visual feedback based on drag progress, such as showing the updated shape preview or snapping guides. /// Used to give real-time visual feedback based on drag progress, such as showing the updated shape preview or snapping guides.
fn dragging_overlays( fn dragging_overlays(&self, mouse_position: DVec2, ctx: &mut GizmoContext, overlay_context: &mut OverlayContext);
&self,
document: &DocumentMessageHandler,
input: &InputPreprocessorMessageHandler,
shape_editor: &mut &mut ShapeState,
mouse_position: DVec2,
overlay_context: &mut OverlayContext,
);
/// Returns `true` if any handle or control point in the gizmo is currently being hovered. /// Returns `true` if any handle or control point in the gizmo is currently being hovered.
fn is_any_gizmo_hovered(&self) -> bool; fn is_any_gizmo_hovered(&self) -> bool;
@@ -8,8 +8,7 @@ use crate::messages::portfolio::document::utility_types::network_interface::{Inp
use crate::messages::tool::common_functionality::gizmos::shape_gizmos::number_of_points_dial::{NumberOfPointsDial, NumberOfPointsDialState}; use crate::messages::tool::common_functionality::gizmos::shape_gizmos::number_of_points_dial::{NumberOfPointsDial, NumberOfPointsDialState};
use crate::messages::tool::common_functionality::gizmos::shape_gizmos::point_radius_handle::{PointRadiusHandle, PointRadiusHandleState}; use crate::messages::tool::common_functionality::gizmos::shape_gizmos::point_radius_handle::{PointRadiusHandle, 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::shape_editor::ShapeState; use crate::messages::tool::common_functionality::shapes::shape_utility::{GizmoContext, ShapeGizmoHandler, star_outline};
use crate::messages::tool::common_functionality::shapes::shape_utility::{ShapeGizmoHandler, star_outline};
use crate::messages::tool::tool_messages::tool_prelude::*; use crate::messages::tool::tool_messages::tool_prelude::*;
use core::f64; use core::f64;
use glam::DAffine2; use glam::DAffine2;
@@ -28,9 +27,9 @@ impl ShapeGizmoHandler for StarGizmoHandler {
self.number_of_points_dial.is_hovering() || self.point_radius_handle.hovered() 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>) { fn handle_state(&mut self, selected_star_layer: LayerNodeIdentifier, mouse_position: DVec2, ctx: &mut GizmoContext) {
self.number_of_points_dial.handle_actions(selected_star_layer, mouse_position, document, responses); self.number_of_points_dial.handle_actions(selected_star_layer, mouse_position, ctx);
self.point_radius_handle.handle_actions(selected_star_layer, document, mouse_position, responses); self.point_radius_handle.handle_actions(selected_star_layer, mouse_position, ctx);
} }
fn handle_click(&mut self) { fn handle_click(&mut self) {
@@ -44,45 +43,30 @@ 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, ctx: &mut GizmoContext) {
if self.number_of_points_dial.is_dragging() { if self.number_of_points_dial.is_dragging() {
self.number_of_points_dial.update_number_of_sides(document, input, responses, drag_start); self.number_of_points_dial.update_number_of_sides(drag_start, ctx);
} }
if self.point_radius_handle.is_dragging_or_snapped() { if self.point_radius_handle.is_dragging_or_snapped() {
self.point_radius_handle.update_inner_radius(document, input, responses, drag_start); self.point_radius_handle.update_inner_radius(drag_start, ctx);
} }
} }
fn overlays( fn overlays(&self, selected_star_layer: Option<LayerNodeIdentifier>, mouse_position: DVec2, ctx: &mut GizmoContext, overlay_context: &mut OverlayContext) {
&self, self.number_of_points_dial.overlays(selected_star_layer, mouse_position, ctx, overlay_context);
document: &DocumentMessageHandler, self.point_radius_handle.overlays(selected_star_layer, ctx, overlay_context);
selected_star_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_star_layer, shape_editor, mouse_position, overlay_context);
self.point_radius_handle.overlays(selected_star_layer, document, input, overlay_context);
star_outline(selected_star_layer, document, overlay_context); star_outline(selected_star_layer, ctx.document, overlay_context);
} }
fn dragging_overlays( fn dragging_overlays(&self, mouse_position: DVec2, ctx: &mut GizmoContext, overlay_context: &mut OverlayContext) {
&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() { if self.number_of_points_dial.is_dragging() {
self.number_of_points_dial.overlays(document, None, shape_editor, mouse_position, overlay_context); self.number_of_points_dial.overlays(None, mouse_position, ctx, overlay_context);
} }
if self.point_radius_handle.is_dragging_or_snapped() { if self.point_radius_handle.is_dragging_or_snapped() {
self.point_radius_handle.overlays(None, document, input, overlay_context); self.point_radius_handle.overlays(None, ctx, overlay_context);
} }
} }
@@ -1,6 +1,9 @@
use super::tool_prelude::*; use super::tool_prelude::*;
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::tool::common_functionality::gizmos::gizmo_manager::GizmoManager;
use crate::messages::tool::common_functionality::operations::circular_repeat::{CircularRepeatOperation, CircularRepeatOperationData}; use crate::messages::tool::common_functionality::operations::circular_repeat::{CircularRepeatOperation, CircularRepeatOperationData};
use crate::messages::tool::common_functionality::shapes::shape_utility::GizmoContext;
#[derive(Debug, Clone, Copy, Eq, PartialEq, Default, serde::Serialize, serde::Deserialize, specta::Type)] #[derive(Debug, Clone, Copy, Eq, PartialEq, Default, serde::Serialize, serde::Deserialize, specta::Type)]
pub enum OperationType { pub enum OperationType {
@@ -52,6 +55,7 @@ pub enum OperationToolMessage {
pub enum OperationToolFsmState { pub enum OperationToolFsmState {
#[default] #[default]
Ready, Ready,
ModifyingGizmo,
Drawing, Drawing,
} }
@@ -124,7 +128,7 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for Oper
Confirm, Confirm,
Abort, Abort,
), ),
OperationToolFsmState::Drawing => actions!(OperationToolMessageDiscriminant; OperationToolFsmState::Drawing | OperationToolFsmState::ModifyingGizmo => actions!(OperationToolMessageDiscriminant;
DragStop, DragStop,
PointerMove, PointerMove,
Confirm, Confirm,
@@ -151,12 +155,24 @@ impl ToolTransition for OperationTool {
pub struct OperationToolData { pub struct OperationToolData {
pub drag_start: DVec2, pub drag_start: DVec2,
pub circular_operation_data: CircularRepeatOperationData, pub circular_operation_data: CircularRepeatOperationData,
gizmo_manager: GizmoManager,
} }
impl OperationToolData { impl OperationToolData {
fn cleanup(&mut self) { fn cleanup(&mut self) {
CircularRepeatOperation::cleanup(self); CircularRepeatOperation::cleanup(self);
} }
fn common_overlays(&self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, overlay_context: &mut OverlayContext) {
for layer in document.network_interface.selected_nodes().selected_visible_and_unlocked_layers(&document.network_interface) {
outline_layer(layer, document, overlay_context);
let Some(hovered_layer) = document.click(input) else { continue };
if hovered_layer != layer {
outline_layer(hovered_layer, document, overlay_context);
}
}
}
} }
impl Fsm for OperationToolFsmState { impl Fsm for OperationToolFsmState {
@@ -171,19 +187,37 @@ impl Fsm for OperationToolFsmState {
tool_options: &Self::ToolOptions, tool_options: &Self::ToolOptions,
responses: &mut VecDeque<Message>, responses: &mut VecDeque<Message>,
) -> Self { ) -> Self {
let ToolActionMessageContext { document, input, .. } = tool_action_data; let ToolActionMessageContext { document, input, shape_editor, .. } = tool_action_data;
let mut ctx = GizmoContext {
document,
input,
responses,
shape_editor,
};
let ToolMessage::Operation(event) = event else { return self }; let ToolMessage::Operation(event) = event else { return self };
match (self, event) { match (self, event) {
(_, OperationToolMessage::Overlays { context: mut overlay_context }) => { (_, OperationToolMessage::Overlays { context: mut overlay_context }) => {
match tool_options.operation_type { if matches!(self, OperationToolFsmState::Ready) {
OperationType::CircularRepeat => CircularRepeatOperation::overlays(&self, tool_data, document, input, &mut overlay_context), tool_data.gizmo_manager.handle_operation_actions(input.mouse.position, &mut ctx);
_ => {} tool_data.gizmo_manager.overlays(input.mouse.position, &mut ctx, &mut overlay_context);
CircularRepeatOperation::overlays(&self, tool_data, document, input, &mut overlay_context);
} }
if matches!(self, OperationToolFsmState::ModifyingGizmo) {
tool_data.gizmo_manager.overlays(input.mouse.position, &mut ctx, &mut overlay_context);
}
tool_data.common_overlays(document, input, &mut overlay_context);
self self
} }
(OperationToolFsmState::Ready, OperationToolMessage::DragStart) => { (OperationToolFsmState::Ready, OperationToolMessage::DragStart) => {
if tool_data.gizmo_manager.handle_click() {
tool_data.drag_start = input.mouse.position;
return OperationToolFsmState::ModifyingGizmo;
}
match tool_options.operation_type { match tool_options.operation_type {
OperationType::CircularRepeat => { OperationType::CircularRepeat => {
CircularRepeatOperation::create_node(tool_data, document, responses, input); CircularRepeatOperation::create_node(tool_data, document, responses, input);
@@ -193,7 +227,7 @@ impl Fsm for OperationToolFsmState {
OperationToolFsmState::Drawing OperationToolFsmState::Drawing
} }
(OperationToolFsmState::Drawing, OperationToolMessage::DragStop) => { (OperationToolFsmState::Drawing | OperationToolFsmState::ModifyingGizmo, OperationToolMessage::DragStop) => {
if tool_data.drag_start.distance(input.mouse.position) < 5. { if tool_data.drag_start.distance(input.mouse.position) < 5. {
responses.add(DocumentMessage::AbortTransaction); responses.add(DocumentMessage::AbortTransaction);
}; };
@@ -216,6 +250,12 @@ impl Fsm for OperationToolFsmState {
OperationToolFsmState::Drawing OperationToolFsmState::Drawing
} }
(OperationToolFsmState::ModifyingGizmo, OperationToolMessage::PointerMove) => {
// Don't add the repeat node unless dragging more that 5 px
tool_data.gizmo_manager.handle_update(tool_data.drag_start, &mut ctx);
OperationToolFsmState::ModifyingGizmo
}
(OperationToolFsmState::Drawing, OperationToolMessage::IncreaseCount) => { (OperationToolFsmState::Drawing, OperationToolMessage::IncreaseCount) => {
match tool_options.operation_type { match tool_options.operation_type {
OperationType::CircularRepeat => CircularRepeatOperation::increase_decrease_count(tool_data, true, document, responses), OperationType::CircularRepeat => CircularRepeatOperation::increase_decrease_count(tool_data, true, document, responses),
@@ -237,7 +277,7 @@ impl Fsm for OperationToolFsmState {
(OperationToolFsmState::Drawing, OperationToolMessage::PointerOutsideViewport) => OperationToolFsmState::Drawing, (OperationToolFsmState::Drawing, OperationToolMessage::PointerOutsideViewport) => OperationToolFsmState::Drawing,
(state, OperationToolMessage::PointerOutsideViewport) => state, (state, OperationToolMessage::PointerOutsideViewport) => state,
(OperationToolFsmState::Drawing, OperationToolMessage::Abort) => { (OperationToolFsmState::Drawing | OperationToolFsmState::ModifyingGizmo, OperationToolMessage::Abort) => {
responses.add(DocumentMessage::AbortTransaction); responses.add(DocumentMessage::AbortTransaction);
OperationToolFsmState::Ready OperationToolFsmState::Ready
} }
@@ -248,11 +288,11 @@ impl Fsm for OperationToolFsmState {
fn update_hints(&self, responses: &mut VecDeque<Message>) { fn update_hints(&self, responses: &mut VecDeque<Message>) {
let hint_data = match self { let hint_data = match self {
OperationToolFsmState::Ready => HintData(vec![HintGroup(vec![ OperationToolFsmState::Ready | OperationToolFsmState::ModifyingGizmo => HintData(vec![HintGroup(vec![
HintInfo::mouse(MouseMotion::Lmb, "Draw Spline"), HintInfo::mouse(MouseMotion::Lmb, "Draw Spline"),
HintInfo::keys([Key::Shift], "Append to Selected Layer").prepend_plus(), HintInfo::keys([Key::Shift], "Append to Selected Layer").prepend_plus(),
])]), ])]),
OperationToolFsmState::Drawing => HintData(vec![ _ => HintData(vec![
HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]), HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]),
HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Extend Spline")]), HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Extend Spline")]),
HintGroup(vec![HintInfo::keys([Key::Enter], "End Spline")]), HintGroup(vec![HintInfo::keys([Key::Enter], "End Spline")]),
@@ -266,3 +306,9 @@ impl Fsm for OperationToolFsmState {
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Default }); responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Default });
} }
} }
fn outline_layer(layer: LayerNodeIdentifier, document: &DocumentMessageHandler, overlay_context: &mut OverlayContext) {
let Some(vector) = document.network_interface.compute_modified_vector(layer) else { return };
let viewport = document.metadata().transform_to_viewport(layer);
overlay_context.outline_vector(&vector, viewport);
}
@@ -14,7 +14,7 @@ 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::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, 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::{GizmoContext, ShapeToolModifierKey, ShapeType, anchor_overlays, transform_cage_overlays};
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};
@@ -477,12 +477,24 @@ impl Fsm for ShapeToolFsmState {
.unwrap_or(input.mouse.position); .unwrap_or(input.mouse.position);
if matches!(self, Self::Ready(_)) && !input.keyboard.key(Key::Control) { if matches!(self, Self::Ready(_)) && !input.keyboard.key(Key::Control) {
tool_data.gizmo_manager.handle_actions(mouse_position, document, responses); let mut ctx = GizmoContext {
tool_data.gizmo_manager.overlays(document, input, shape_editor, mouse_position, &mut overlay_context); document,
input,
responses,
shape_editor,
};
tool_data.gizmo_manager.handle_actions(mouse_position, &mut ctx);
tool_data.gizmo_manager.overlays(mouse_position, &mut ctx, &mut overlay_context);
} }
if matches!(self, ShapeToolFsmState::ModifyingGizmo) && !input.keyboard.key(Key::Control) { if matches!(self, ShapeToolFsmState::ModifyingGizmo) && !input.keyboard.key(Key::Control) {
tool_data.gizmo_manager.dragging_overlays(document, input, shape_editor, mouse_position, &mut overlay_context); let mut ctx = GizmoContext {
document,
input,
responses,
shape_editor,
};
tool_data.gizmo_manager.dragging_overlays(mouse_position, &mut ctx, &mut overlay_context);
let cursor = tool_data.gizmo_manager.mouse_cursor_icon().unwrap_or(MouseCursorIcon::Crosshair); let cursor = tool_data.gizmo_manager.mouse_cursor_icon().unwrap_or(MouseCursorIcon::Crosshair);
tool_data.cursor = cursor; tool_data.cursor = cursor;
responses.add(FrontendMessage::UpdateMouseCursor { cursor }); responses.add(FrontendMessage::UpdateMouseCursor { cursor });
@@ -541,9 +553,15 @@ impl Fsm for ShapeToolFsmState {
} }
if matches!(self, ShapeToolFsmState::Drawing(_) | ShapeToolFsmState::DraggingLineEndpoints) { if matches!(self, ShapeToolFsmState::Drawing(_) | ShapeToolFsmState::DraggingLineEndpoints) {
let mut ctx = GizmoContext {
document,
input,
responses,
shape_editor,
};
Line::overlays(document, tool_data, &mut overlay_context); Line::overlays(document, tool_data, &mut overlay_context);
if tool_options.shape_type == ShapeType::Circle { if tool_options.shape_type == ShapeType::Circle {
tool_data.gizmo_manager.overlays(document, input, shape_editor, mouse_position, &mut overlay_context); tool_data.gizmo_manager.overlays(mouse_position, &mut ctx, &mut overlay_context);
} }
} }
@@ -822,7 +840,13 @@ impl Fsm for ShapeToolFsmState {
self self
} }
(ShapeToolFsmState::ModifyingGizmo, ShapeToolMessage::PointerMove { .. }) => { (ShapeToolFsmState::ModifyingGizmo, ShapeToolMessage::PointerMove { .. }) => {
tool_data.gizmo_manager.handle_update(tool_data.data.viewport_drag_start(document), document, input, responses); let mut ctx = GizmoContext {
document,
input,
responses,
shape_editor,
};
tool_data.gizmo_manager.handle_update(tool_data.data.viewport_drag_start(document), &mut ctx);
responses.add(OverlaysMessage::Draw); responses.add(OverlaysMessage::Draw);