Add gizmos for interacting with the Spiral node (#2851)
* made spiral node * number of turns in decimal and arc-angle implementation * logarithmic spiral * unified log and arc spiral into spiral node * add spiral shape in shape tool * fix min value and degree unit * make it compile * impl turns handle gizmo * chore : Refactoring PR #2851 for current code base with some fixes * Code review --------- Co-authored-by: Keavon Chambers <keavon@keavon.com> Co-authored-by: Annonnymmousss <jatin02012006@gmail.com>
This commit is contained in:
parent
cd241095a2
commit
ea68d62ec4
|
|
@ -10,6 +10,7 @@ use crate::messages::tool::common_functionality::shapes::circle_shape::CircleGiz
|
||||||
use crate::messages::tool::common_functionality::shapes::grid_shape::GridGizmoHandler;
|
use crate::messages::tool::common_functionality::shapes::grid_shape::GridGizmoHandler;
|
||||||
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::ShapeGizmoHandler;
|
||||||
|
use crate::messages::tool::common_functionality::shapes::spiral_shape::SpiralGizmoHandler;
|
||||||
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::VecDeque;
|
||||||
|
|
@ -30,6 +31,7 @@ pub enum ShapeGizmoHandlers {
|
||||||
Arc(ArcGizmoHandler),
|
Arc(ArcGizmoHandler),
|
||||||
Circle(CircleGizmoHandler),
|
Circle(CircleGizmoHandler),
|
||||||
Grid(GridGizmoHandler),
|
Grid(GridGizmoHandler),
|
||||||
|
Spiral(SpiralGizmoHandler),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ShapeGizmoHandlers {
|
impl ShapeGizmoHandlers {
|
||||||
|
|
@ -42,6 +44,7 @@ impl ShapeGizmoHandlers {
|
||||||
Self::Arc(_) => "arc",
|
Self::Arc(_) => "arc",
|
||||||
Self::Circle(_) => "circle",
|
Self::Circle(_) => "circle",
|
||||||
Self::Grid(_) => "grid",
|
Self::Grid(_) => "grid",
|
||||||
|
Self::Spiral(_) => "spiral",
|
||||||
Self::None => "none",
|
Self::None => "none",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -54,6 +57,7 @@ impl ShapeGizmoHandlers {
|
||||||
Self::Arc(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::Circle(h) => h.handle_state(layer, mouse_position, document, responses),
|
||||||
Self::Grid(h) => h.handle_state(layer, mouse_position, document, responses),
|
Self::Grid(h) => h.handle_state(layer, mouse_position, document, responses),
|
||||||
|
Self::Spiral(h) => h.handle_state(layer, mouse_position, document, responses),
|
||||||
Self::None => {}
|
Self::None => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -66,6 +70,7 @@ impl ShapeGizmoHandlers {
|
||||||
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::Grid(h) => h.is_any_gizmo_hovered(),
|
Self::Grid(h) => h.is_any_gizmo_hovered(),
|
||||||
|
Self::Spiral(h) => h.is_any_gizmo_hovered(),
|
||||||
Self::None => false,
|
Self::None => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -78,6 +83,7 @@ impl ShapeGizmoHandlers {
|
||||||
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::Grid(h) => h.handle_click(),
|
Self::Grid(h) => h.handle_click(),
|
||||||
|
Self::Spiral(h) => h.handle_click(),
|
||||||
Self::None => {}
|
Self::None => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -90,6 +96,7 @@ impl ShapeGizmoHandlers {
|
||||||
Self::Arc(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::Circle(h) => h.handle_update(drag_start, document, input, responses),
|
||||||
Self::Grid(h) => h.handle_update(drag_start, document, input, responses),
|
Self::Grid(h) => h.handle_update(drag_start, document, input, responses),
|
||||||
|
Self::Spiral(h) => h.handle_update(drag_start, document, input, responses),
|
||||||
Self::None => {}
|
Self::None => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -102,6 +109,7 @@ impl ShapeGizmoHandlers {
|
||||||
Self::Arc(h) => h.cleanup(),
|
Self::Arc(h) => h.cleanup(),
|
||||||
Self::Circle(h) => h.cleanup(),
|
Self::Circle(h) => h.cleanup(),
|
||||||
Self::Grid(h) => h.cleanup(),
|
Self::Grid(h) => h.cleanup(),
|
||||||
|
Self::Spiral(h) => h.cleanup(),
|
||||||
Self::None => {}
|
Self::None => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -122,6 +130,7 @@ impl ShapeGizmoHandlers {
|
||||||
Self::Arc(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),
|
||||||
Self::Circle(h) => h.overlays(document, layer, input, shape_editor, mouse_position, overlay_context),
|
Self::Circle(h) => h.overlays(document, layer, input, shape_editor, mouse_position, overlay_context),
|
||||||
Self::Grid(h) => h.overlays(document, layer, input, shape_editor, mouse_position, overlay_context),
|
Self::Grid(h) => h.overlays(document, layer, input, shape_editor, mouse_position, overlay_context),
|
||||||
|
Self::Spiral(h) => h.overlays(document, layer, input, shape_editor, mouse_position, overlay_context),
|
||||||
Self::None => {}
|
Self::None => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -141,6 +150,7 @@ impl ShapeGizmoHandlers {
|
||||||
Self::Arc(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),
|
||||||
Self::Circle(h) => h.dragging_overlays(document, input, shape_editor, mouse_position, overlay_context),
|
Self::Circle(h) => h.dragging_overlays(document, input, shape_editor, mouse_position, overlay_context),
|
||||||
Self::Grid(h) => h.dragging_overlays(document, input, shape_editor, mouse_position, overlay_context),
|
Self::Grid(h) => h.dragging_overlays(document, input, shape_editor, mouse_position, overlay_context),
|
||||||
|
Self::Spiral(h) => h.dragging_overlays(document, input, shape_editor, mouse_position, overlay_context),
|
||||||
Self::None => {}
|
Self::None => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -152,6 +162,7 @@ impl ShapeGizmoHandlers {
|
||||||
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::Grid(h) => h.mouse_cursor_icon(),
|
Self::Grid(h) => h.mouse_cursor_icon(),
|
||||||
|
Self::Spiral(h) => h.mouse_cursor_icon(),
|
||||||
Self::None => None,
|
Self::None => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -199,6 +210,10 @@ impl GizmoManager {
|
||||||
if graph_modification_utils::get_grid_id(layer, &document.network_interface).is_some() {
|
if graph_modification_utils::get_grid_id(layer, &document.network_interface).is_some() {
|
||||||
return Some(ShapeGizmoHandlers::Grid(GridGizmoHandler::default()));
|
return Some(ShapeGizmoHandlers::Grid(GridGizmoHandler::default()));
|
||||||
}
|
}
|
||||||
|
// Spiral
|
||||||
|
if graph_modification_utils::get_spiral_id(layer, &document.network_interface).is_some() {
|
||||||
|
return Some(ShapeGizmoHandlers::Spiral(SpiralGizmoHandler::default()));
|
||||||
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,4 +2,5 @@ pub mod circle_arc_radius_handle;
|
||||||
pub mod grid_rows_columns_gizmo;
|
pub mod grid_rows_columns_gizmo;
|
||||||
pub mod number_of_points_dial;
|
pub mod number_of_points_dial;
|
||||||
pub mod point_radius_handle;
|
pub mod point_radius_handle;
|
||||||
|
pub mod spiral_turns_handle;
|
||||||
pub mod sweep_angle_gizmo;
|
pub mod sweep_angle_gizmo;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,227 @@
|
||||||
|
use crate::consts::{COLOR_OVERLAY_RED, POINT_RADIUS_HANDLE_SNAP_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;
|
||||||
|
use crate::messages::tool::common_functionality::shape_editor::ShapeState;
|
||||||
|
use crate::messages::tool::common_functionality::shapes::shape_utility::extract_spiral_parameters;
|
||||||
|
use crate::messages::tool::common_functionality::shapes::spiral_shape::calculate_spiral_endpoints;
|
||||||
|
use glam::DVec2;
|
||||||
|
use graph_craft::document::NodeInput;
|
||||||
|
use graph_craft::document::value::TaggedValue;
|
||||||
|
use graphene_std::NodeInputDecleration;
|
||||||
|
use graphene_std::subpath::{calculate_growth_factor, spiral_point};
|
||||||
|
use graphene_std::vector::misc::SpiralType;
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
use std::f64::consts::TAU;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, PartialEq)]
|
||||||
|
pub enum GizmoType {
|
||||||
|
#[default]
|
||||||
|
None,
|
||||||
|
Start,
|
||||||
|
End,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, PartialEq)]
|
||||||
|
pub enum SpiralTurnsState {
|
||||||
|
#[default]
|
||||||
|
Inactive,
|
||||||
|
Hover,
|
||||||
|
Dragging,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct SpiralTurns {
|
||||||
|
pub layer: Option<LayerNodeIdentifier>,
|
||||||
|
pub handle_state: SpiralTurnsState,
|
||||||
|
initial_turns: f64,
|
||||||
|
initial_outer_radius: f64,
|
||||||
|
initial_inner_radius: f64,
|
||||||
|
initial_growth_factor: f64,
|
||||||
|
initial_start_angle: f64,
|
||||||
|
previous_mouse_position: DVec2,
|
||||||
|
total_angle_delta: f64,
|
||||||
|
gizmo_type: GizmoType,
|
||||||
|
spiral_type: SpiralType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SpiralTurns {
|
||||||
|
pub fn cleanup(&mut self) {
|
||||||
|
self.handle_state = SpiralTurnsState::Inactive;
|
||||||
|
self.total_angle_delta = 0.;
|
||||||
|
self.gizmo_type = GizmoType::None;
|
||||||
|
self.layer = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_state(&mut self, state: SpiralTurnsState) {
|
||||||
|
self.handle_state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hovered(&self) -> bool {
|
||||||
|
self.handle_state == SpiralTurnsState::Hover
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_dragging(&self) -> bool {
|
||||||
|
self.handle_state == SpiralTurnsState::Dragging
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn store_initial_parameters(
|
||||||
|
&mut self,
|
||||||
|
layer: LayerNodeIdentifier,
|
||||||
|
inner_radius: f64,
|
||||||
|
outer_radius: f64,
|
||||||
|
turns: f64,
|
||||||
|
start_angle: f64,
|
||||||
|
mouse_position: DVec2,
|
||||||
|
gizmo_type: GizmoType,
|
||||||
|
spiral_type: SpiralType,
|
||||||
|
) {
|
||||||
|
self.layer = Some(layer);
|
||||||
|
self.initial_turns = turns;
|
||||||
|
self.initial_growth_factor = calculate_growth_factor(inner_radius, turns, outer_radius, spiral_type);
|
||||||
|
self.initial_inner_radius = inner_radius;
|
||||||
|
self.initial_outer_radius = outer_radius;
|
||||||
|
self.initial_start_angle = start_angle;
|
||||||
|
self.previous_mouse_position = mouse_position;
|
||||||
|
self.spiral_type = spiral_type;
|
||||||
|
self.gizmo_type = gizmo_type;
|
||||||
|
self.update_state(SpiralTurnsState::Hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_actions(&mut self, layer: LayerNodeIdentifier, mouse_position: DVec2, document: &DocumentMessageHandler, _responses: &mut VecDeque<Message>) {
|
||||||
|
let viewport = document.metadata().transform_to_viewport(layer);
|
||||||
|
|
||||||
|
match &self.handle_state {
|
||||||
|
SpiralTurnsState::Inactive => {
|
||||||
|
if let Some((spiral_type, start_angle, inner_radius, outer_radius, turns, _)) = extract_spiral_parameters(layer, document) {
|
||||||
|
let growth_factor = calculate_growth_factor(inner_radius, turns, outer_radius, spiral_type);
|
||||||
|
let end_point = viewport.transform_point2(spiral_point(turns * TAU + start_angle.to_radians(), inner_radius, growth_factor, spiral_type));
|
||||||
|
let start_point = viewport.transform_point2(spiral_point(0. + start_angle.to_radians(), inner_radius, growth_factor, spiral_type));
|
||||||
|
|
||||||
|
if mouse_position.distance(end_point) < POINT_RADIUS_HANDLE_SNAP_THRESHOLD {
|
||||||
|
self.store_initial_parameters(layer, inner_radius, outer_radius, turns, start_angle, mouse_position, GizmoType::End, spiral_type);
|
||||||
|
} else if mouse_position.distance(start_point) < POINT_RADIUS_HANDLE_SNAP_THRESHOLD {
|
||||||
|
self.store_initial_parameters(layer, inner_radius, outer_radius, turns, start_angle, mouse_position, GizmoType::Start, spiral_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SpiralTurnsState::Hover | SpiralTurnsState::Dragging => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn overlays(&self, document: &DocumentMessageHandler, layer: Option<LayerNodeIdentifier>, _shape_editor: &mut &mut ShapeState, _mouse_position: DVec2, overlay_context: &mut OverlayContext) {
|
||||||
|
let Some(layer) = layer.or(self.layer) else { return };
|
||||||
|
let viewport = document.metadata().transform_to_viewport(layer);
|
||||||
|
|
||||||
|
match &self.handle_state {
|
||||||
|
SpiralTurnsState::Inactive => {
|
||||||
|
if let Some((p1, p2)) = calculate_spiral_endpoints(layer, document, viewport, 0.).zip(calculate_spiral_endpoints(layer, document, viewport, TAU)) {
|
||||||
|
overlay_context.manipulator_handle(p1, false, None);
|
||||||
|
overlay_context.manipulator_handle(p2, false, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SpiralTurnsState::Hover | SpiralTurnsState::Dragging => {
|
||||||
|
// Is true only when hovered over the gizmo
|
||||||
|
let selected = self.layer.is_some();
|
||||||
|
let angle = match self.gizmo_type {
|
||||||
|
GizmoType::End => TAU,
|
||||||
|
GizmoType::Start => 0.,
|
||||||
|
GizmoType::None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(endpoint) = calculate_spiral_endpoints(layer, document, viewport, angle) {
|
||||||
|
overlay_context.manipulator_handle(endpoint, selected, Some(COLOR_OVERLAY_RED));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_number_of_turns(&mut self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) {
|
||||||
|
use graphene_std::vector::generator_nodes::spiral::*;
|
||||||
|
|
||||||
|
let Some(layer) = self.layer else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let viewport = document.metadata().transform_to_viewport(layer);
|
||||||
|
let center = viewport.transform_point2(DVec2::ZERO);
|
||||||
|
|
||||||
|
let angle_delta = viewport
|
||||||
|
.inverse()
|
||||||
|
.transform_vector2(input.mouse.position - center)
|
||||||
|
.angle_to(viewport.inverse().transform_vector2(self.previous_mouse_position - center))
|
||||||
|
.to_degrees();
|
||||||
|
|
||||||
|
// Skip update if angle calculation produced NaN or infinity (can happen when mouse is at center)
|
||||||
|
// Also skip very small angle changes to reduce jitter near center
|
||||||
|
if !angle_delta.is_finite() || angle_delta.abs() < 0.5 {
|
||||||
|
self.previous_mouse_position = input.mouse.position;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increase the number of turns and outer radius in unison such that growth and tightness remain same
|
||||||
|
let total_delta = self.total_angle_delta + angle_delta;
|
||||||
|
// Convert the total angle (in degrees) to number of full turns
|
||||||
|
let turns_delta = total_delta / 360.;
|
||||||
|
|
||||||
|
// Calculate the new outer radius based on spiral type and turn change
|
||||||
|
let outer_radius_change = match self.spiral_type {
|
||||||
|
SpiralType::Archimedean => turns_delta * (self.initial_growth_factor) * TAU,
|
||||||
|
SpiralType::Logarithmic => self.initial_outer_radius * ((self.initial_growth_factor * TAU * turns_delta).exp() - 1.),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Skip if outer_radius calculation produced invalid values
|
||||||
|
if !outer_radius_change.is_finite() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(node_id) = graph_modification_utils::get_spiral_id(layer, &document.network_interface) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
match self.gizmo_type {
|
||||||
|
GizmoType::Start => {
|
||||||
|
let sign = -1.;
|
||||||
|
let new_turns = (self.initial_turns + turns_delta * sign).max(0.5);
|
||||||
|
let new_outer_radius = (self.initial_outer_radius + outer_radius_change * sign).max(0.1);
|
||||||
|
|
||||||
|
responses.add(NodeGraphMessage::SetInput {
|
||||||
|
input_connector: InputConnector::node(node_id, StartAngleInput::INDEX),
|
||||||
|
input: NodeInput::value(TaggedValue::F64(self.initial_start_angle + total_delta), false),
|
||||||
|
});
|
||||||
|
responses.add(NodeGraphMessage::SetInput {
|
||||||
|
input_connector: InputConnector::node(node_id, TurnsInput::INDEX),
|
||||||
|
input: NodeInput::value(TaggedValue::F64(new_turns), false),
|
||||||
|
});
|
||||||
|
responses.add(NodeGraphMessage::SetInput {
|
||||||
|
input_connector: InputConnector::node(node_id, OuterRadiusInput::INDEX),
|
||||||
|
input: NodeInput::value(TaggedValue::F64(new_outer_radius), false),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
GizmoType::End => {
|
||||||
|
let new_turns = (self.initial_turns + turns_delta).max(0.5);
|
||||||
|
let new_outer_radius = (self.initial_outer_radius + outer_radius_change).max(0.1);
|
||||||
|
|
||||||
|
responses.add(NodeGraphMessage::SetInput {
|
||||||
|
input_connector: InputConnector::node(node_id, TurnsInput::INDEX),
|
||||||
|
input: NodeInput::value(TaggedValue::F64(new_turns), false),
|
||||||
|
});
|
||||||
|
responses.add(NodeGraphMessage::SetInput {
|
||||||
|
input_connector: InputConnector::node(node_id, OuterRadiusInput::INDEX),
|
||||||
|
input: NodeInput::value(TaggedValue::F64(new_outer_radius), false),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
GizmoType::None => {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||||
|
self.total_angle_delta += angle_delta;
|
||||||
|
self.previous_mouse_position = input.mouse.position;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -18,7 +18,7 @@ use graph_craft::document::value::TaggedValue;
|
||||||
use graphene_std::NodeInputDecleration;
|
use graphene_std::NodeInputDecleration;
|
||||||
use graphene_std::subpath::{self, Subpath};
|
use graphene_std::subpath::{self, Subpath};
|
||||||
use graphene_std::vector::click_target::ClickTargetType;
|
use graphene_std::vector::click_target::ClickTargetType;
|
||||||
use graphene_std::vector::misc::{ArcType, GridType, dvec2_to_point};
|
use graphene_std::vector::misc::{ArcType, GridType, SpiralType, dvec2_to_point};
|
||||||
use kurbo::{BezPath, PathEl, Shape};
|
use kurbo::{BezPath, PathEl, Shape};
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::f64::consts::{PI, TAU};
|
use std::f64::consts::{PI, TAU};
|
||||||
|
|
@ -293,6 +293,35 @@ pub fn extract_arc_parameters(layer: Option<LayerNodeIdentifier>, document: &Doc
|
||||||
Some((radius, start_angle, sweep_angle, arc_type))
|
Some((radius, start_angle, sweep_angle, arc_type))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Extract the node input values of spiral.
|
||||||
|
/// Returns an option of (spiral type, start angle, inner radius, outer radius, turns, angle resolution).
|
||||||
|
pub fn extract_spiral_parameters(layer: LayerNodeIdentifier, document: &DocumentMessageHandler) -> Option<(SpiralType, f64, f64, f64, f64, f64)> {
|
||||||
|
use graphene_std::vector::generator_nodes::spiral::*;
|
||||||
|
|
||||||
|
let node_inputs = NodeGraphLayer::new(layer, &document.network_interface).find_node_inputs(&DefinitionIdentifier::ProtoNode(graphene_std::vector::generator_nodes::spiral::IDENTIFIER))?;
|
||||||
|
|
||||||
|
let (
|
||||||
|
Some(&TaggedValue::SpiralType(spiral_type)),
|
||||||
|
Some(&TaggedValue::F64(start_angle)),
|
||||||
|
Some(&TaggedValue::F64(inner_radius)),
|
||||||
|
Some(&TaggedValue::F64(outer_radius)),
|
||||||
|
Some(&TaggedValue::F64(turns)),
|
||||||
|
Some(&TaggedValue::F64(angle_resolution)),
|
||||||
|
) = (
|
||||||
|
node_inputs.get(SpiralTypeInput::INDEX)?.as_value(),
|
||||||
|
node_inputs.get(StartAngleInput::INDEX)?.as_value(),
|
||||||
|
node_inputs.get(InnerRadiusInput::INDEX)?.as_value(),
|
||||||
|
node_inputs.get(OuterRadiusInput::INDEX)?.as_value(),
|
||||||
|
node_inputs.get(TurnsInput::INDEX)?.as_value(),
|
||||||
|
node_inputs.get(AngularResolutionInput::INDEX)?.as_value(),
|
||||||
|
)
|
||||||
|
else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
Some((spiral_type, start_angle, inner_radius, outer_radius, turns, angle_resolution))
|
||||||
|
}
|
||||||
|
|
||||||
/// Calculate the viewport positions of arc endpoints
|
/// Calculate the viewport positions of arc endpoints
|
||||||
pub fn arc_end_points(layer: Option<LayerNodeIdentifier>, document: &DocumentMessageHandler) -> Option<(DVec2, DVec2)> {
|
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)?;
|
let (radius, start_angle, sweep_angle, _) = extract_arc_parameters(Some(layer?), document)?;
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,14 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::messages::frontend::utility_types::MouseCursorIcon;
|
||||||
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::{DefinitionIdentifier, resolve_document_node_type};
|
use crate::messages::portfolio::document::node_graph::document_node_definitions::{DefinitionIdentifier, 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::spiral_turns_handle::{SpiralTurns, SpiralTurnsState};
|
||||||
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::shape_editor::ShapeState;
|
||||||
|
use crate::messages::tool::common_functionality::shapes::shape_utility::{ShapeGizmoHandler, extract_spiral_parameters};
|
||||||
use crate::messages::tool::common_functionality::snapping::{SnapCandidatePoint, SnapData, SnapTypeConfiguration};
|
use crate::messages::tool::common_functionality::snapping::{SnapCandidatePoint, SnapData, SnapTypeConfiguration};
|
||||||
use crate::messages::tool::tool_messages::shape_tool::ShapeOptionsUpdate;
|
use crate::messages::tool::tool_messages::shape_tool::ShapeOptionsUpdate;
|
||||||
use crate::messages::tool::tool_messages::tool_prelude::*;
|
use crate::messages::tool::tool_messages::tool_prelude::*;
|
||||||
|
|
@ -11,9 +16,82 @@ use glam::DAffine2;
|
||||||
use graph_craft::document::NodeInput;
|
use graph_craft::document::NodeInput;
|
||||||
use graph_craft::document::value::TaggedValue;
|
use graph_craft::document::value::TaggedValue;
|
||||||
use graphene_std::NodeInputDecleration;
|
use graphene_std::NodeInputDecleration;
|
||||||
|
use graphene_std::subpath::{calculate_growth_factor, spiral_point};
|
||||||
use graphene_std::vector::misc::SpiralType;
|
use graphene_std::vector::misc::SpiralType;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct SpiralGizmoHandler {
|
||||||
|
turns_handle: SpiralTurns,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ShapeGizmoHandler for SpiralGizmoHandler {
|
||||||
|
fn is_any_gizmo_hovered(&self) -> bool {
|
||||||
|
self.turns_handle.hovered()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_state(&mut self, selected_spiral_layer: LayerNodeIdentifier, mouse_position: DVec2, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
|
||||||
|
self.turns_handle.handle_actions(selected_spiral_layer, mouse_position, document, responses);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_click(&mut self) {
|
||||||
|
if self.turns_handle.hovered() {
|
||||||
|
self.turns_handle.update_state(SpiralTurnsState::Dragging);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_update(&mut self, _drag_start: DVec2, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) {
|
||||||
|
if self.turns_handle.is_dragging() {
|
||||||
|
self.turns_handle.update_number_of_turns(document, input, responses);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn overlays(
|
||||||
|
&self,
|
||||||
|
document: &DocumentMessageHandler,
|
||||||
|
selected_spiral_layer: Option<LayerNodeIdentifier>,
|
||||||
|
_input: &InputPreprocessorMessageHandler,
|
||||||
|
shape_editor: &mut &mut ShapeState,
|
||||||
|
mouse_position: DVec2,
|
||||||
|
overlay_context: &mut OverlayContext,
|
||||||
|
) {
|
||||||
|
self.turns_handle.overlays(document, selected_spiral_layer, shape_editor, mouse_position, overlay_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dragging_overlays(
|
||||||
|
&self,
|
||||||
|
document: &DocumentMessageHandler,
|
||||||
|
_input: &InputPreprocessorMessageHandler,
|
||||||
|
shape_editor: &mut &mut ShapeState,
|
||||||
|
mouse_position: DVec2,
|
||||||
|
overlay_context: &mut OverlayContext,
|
||||||
|
) {
|
||||||
|
if self.turns_handle.is_dragging() {
|
||||||
|
self.turns_handle.overlays(document, None, shape_editor, mouse_position, overlay_context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mouse_cursor_icon(&self) -> Option<MouseCursorIcon> {
|
||||||
|
if self.turns_handle.hovered() || self.turns_handle.is_dragging() {
|
||||||
|
return Some(MouseCursorIcon::Default);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cleanup(&mut self) {
|
||||||
|
self.turns_handle.cleanup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculates the position of a spiral endpoint at a given angle offset (0 = start, TAU = end).
|
||||||
|
pub fn calculate_spiral_endpoints(layer: LayerNodeIdentifier, document: &DocumentMessageHandler, viewport: DAffine2, theta: f64) -> Option<DVec2> {
|
||||||
|
let (spiral_type, start_angle, a, outer_radius, turns, _) = extract_spiral_parameters(layer, document)?;
|
||||||
|
let b = calculate_growth_factor(a, turns, outer_radius, spiral_type);
|
||||||
|
let theta = turns * theta + start_angle.to_radians();
|
||||||
|
|
||||||
|
Some(viewport.transform_point2(spiral_point(theta, a, b, spiral_type)))
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Spiral;
|
pub struct Spiral;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -348,7 +348,7 @@ impl<PointId: Identifier> Subpath<PointId> {
|
||||||
let mut prev_in_handle = None;
|
let mut prev_in_handle = None;
|
||||||
let theta_end = turns * std::f64::consts::TAU + start_angle;
|
let theta_end = turns * std::f64::consts::TAU + start_angle;
|
||||||
|
|
||||||
let b = calculate_b(a, turns, outer_radius, spiral_type);
|
let b = calculate_growth_factor(a, turns, outer_radius, spiral_type);
|
||||||
|
|
||||||
let mut theta = start_angle;
|
let mut theta = start_angle;
|
||||||
while theta < theta_end {
|
while theta < theta_end {
|
||||||
|
|
@ -381,7 +381,7 @@ impl<PointId: Identifier> Subpath<PointId> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn calculate_b(a: f64, turns: f64, outer_radius: f64, spiral_type: SpiralType) -> f64 {
|
pub fn calculate_growth_factor(a: f64, turns: f64, outer_radius: f64, spiral_type: SpiralType) -> f64 {
|
||||||
match spiral_type {
|
match spiral_type {
|
||||||
SpiralType::Archimedean => {
|
SpiralType::Archimedean => {
|
||||||
let total_theta = turns * TAU;
|
let total_theta = turns * TAU;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue