fixed wrapping need to fix snapping and overlays

This commit is contained in:
0SlowPoke0 2025-07-04 19:45:02 +05:30
parent cdb1bf7707
commit 0b548e5281
2 changed files with 146 additions and 34 deletions

View file

@ -13,6 +13,7 @@ use crate::messages::{
prelude::{DocumentMessageHandler, FrontendMessage}, prelude::{DocumentMessageHandler, FrontendMessage},
}; };
use glam::DVec2; use glam::DVec2;
use graph_craft::document::NodeId;
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::collections::VecDeque;
@ -41,6 +42,7 @@ pub struct SweepAngleGizmo {
endpoint: EndpointType, endpoint: EndpointType,
initial_start_angle: f64, initial_start_angle: f64,
initial_sweep_angle: f64, initial_sweep_angle: f64,
initial_start_point: DVec2,
previous_mouse_position: DVec2, previous_mouse_position: DVec2,
total_angle_delta: f64, total_angle_delta: f64,
snap_angles: Vec<f64>, snap_angles: Vec<f64>,
@ -203,6 +205,7 @@ impl SweepAngleGizmo {
let offset_angle = initial_vector.to_angle() + tilt_offset; let offset_angle = initial_vector.to_angle() + tilt_offset;
let angle = initial_vector.angle_to(final_vector).to_degrees(); let angle = initial_vector.angle_to(final_vector).to_degrees();
log::info!("angle {:?}", angle);
let display_angle = calculate_display_angle(angle); let display_angle = calculate_display_angle(angle);
let text = format!("{}°", format_rounded(display_angle, 2)); let text = format!("{}°", format_rounded(display_angle, 2));
@ -224,52 +227,161 @@ impl SweepAngleGizmo {
return; return;
}; };
let Some((_, start_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;
}; };
let viewport = document.metadata().transform_to_viewport(layer); let viewport = document.metadata().transform_to_viewport(layer);
let angle = self.total_angle_delta let angle_delta = viewport
+ viewport .inverse()
.inverse() .transform_point2(self.previous_mouse_position)
.transform_point2(self.previous_mouse_position) .angle_to(viewport.inverse().transform_point2(input.mouse.position))
.angle_to(viewport.inverse().transform_point2(input.mouse.position)) .to_degrees();
.to_degrees(); let angle = self.total_angle_delta + angle_delta;
let Some(node_id) = graph_modification_utils::get_arc_id(layer, &document.network_interface) else { let Some(node_id) = graph_modification_utils::get_arc_id(layer, &document.network_interface) else {
return; return;
}; };
self.update_state(SweepAngleGizmoState::Dragging); self.update_state(SweepAngleGizmoState::Dragging);
if self.endpoint == EndpointType::End {
let mut total = angle;
if let Some(snapped_delta) = self.check_snapping(start_angle, self.initial_sweep_angle + angle) {
total += snapped_delta;
self.update_state(SweepAngleGizmoState::Snapped);
}
responses.add(NodeGraphMessage::SetInput {
input_connector: InputConnector::node(node_id, 3),
input: NodeInput::value(TaggedValue::F64(self.initial_sweep_angle + total), false),
});
} else {
let sign = angle.signum() * -1.;
let mut total = angle;
if let Some(snapped_delta) = self.check_snapping(self.initial_start_angle + angle, self.initial_sweep_angle + total.abs() * sign) { match self.endpoint {
total += snapped_delta; EndpointType::Start => {
self.update_state(SweepAngleGizmoState::Snapped); // Dragging start changes both start and sweep
let sign = angle.signum() * -1.;
let mut total = angle;
let new_start_angle = self.initial_start_angle + total;
let new_sweep_angle = self.initial_sweep_angle + total.abs() * sign;
// Clamp sweep angle to 360°
if new_sweep_angle > 360. {
let wrapped = new_sweep_angle % 360.;
self.total_angle_delta = -wrapped;
// Remaining drag gets passed to the end endpoint
let rest_angle = angle_delta + wrapped;
self.endpoint = EndpointType::End;
self.initial_sweep_angle = 360.;
self.initial_start_angle = current_start_angle + rest_angle;
self.apply_arc_update(node_id, self.initial_start_angle, self.initial_sweep_angle - wrapped, input, responses);
return;
}
if new_sweep_angle < 0. {
let rest_angle = angle_delta + new_sweep_angle;
self.total_angle_delta = new_sweep_angle.abs();
self.endpoint = EndpointType::End;
self.initial_sweep_angle = 0.;
self.initial_start_angle = current_start_angle + rest_angle;
self.apply_arc_update(node_id, self.initial_start_angle, new_sweep_angle.abs(), input, responses);
return;
}
// Wrap start angle > 180° back into [-180°, 180°] and adjust sweep
if new_start_angle > 180. {
let overflow = new_start_angle % 180.;
let rest_angle = angle_delta - overflow;
// We wrap the angle back into [-180°, 180°] range by jumping from +180° to -180°
// Example: dragging past 190° becomes -170°, and we subtract the overshoot from sweep
// Sweep angle must shrink to maintain consistent arc
self.total_angle_delta = rest_angle;
self.initial_start_angle = -180.;
self.initial_sweep_angle = current_sweep_angle - rest_angle;
self.apply_arc_update(node_id, self.initial_start_angle + overflow, self.initial_sweep_angle - overflow, input, responses);
return;
}
// Wrap start angle < -180° back into [-180°, 180°] and adjust sweep
if new_start_angle < -180. {
let underflow = new_start_angle % 180.;
let rest_angle = angle_delta - underflow;
// We wrap the angle back into [-180°, 180°] by jumping from -190° to +170°
// Sweep must grow to reflect continued clockwise drag past -180°
// Start angle flips from -190° to +170°, and sweep increases accordingly
self.total_angle_delta = underflow;
self.initial_start_angle = 180.;
self.initial_sweep_angle = current_sweep_angle + rest_angle.abs();
self.apply_arc_update(node_id, self.initial_start_angle + underflow, self.initial_sweep_angle + underflow.abs(), input, responses);
return;
}
if let Some(snapped_delta) = self.check_snapping(self.initial_start_angle + angle, self.initial_sweep_angle + total.abs() * sign) {
total += snapped_delta;
self.update_state(SweepAngleGizmoState::Snapped);
}
self.total_angle_delta = angle;
self.apply_arc_update(node_id, self.initial_start_angle + total, self.initial_sweep_angle + total.abs() * sign, input, responses);
} }
responses.add(NodeGraphMessage::SetInput { EndpointType::End => {
input_connector: InputConnector::node(node_id, 2), // Dragging the end only changes sweep angle
input: NodeInput::value(TaggedValue::F64(self.initial_start_angle + total), false),
}); let mut total = angle;
responses.add(NodeGraphMessage::SetInput { let new_sweep_angle = self.initial_sweep_angle + angle;
input_connector: InputConnector::node(node_id, 3),
input: NodeInput::value(TaggedValue::F64(self.initial_sweep_angle + total.abs() * sign), false), // Clamp sweep angle below 0°, switch to start
}); if new_sweep_angle < 0. {
let delta = angle_delta - current_sweep_angle;
let sign = delta.signum() * -1.;
self.initial_sweep_angle = 0.;
self.total_angle_delta = delta;
self.endpoint = EndpointType::Start;
self.apply_arc_update(node_id, self.initial_start_angle + delta, self.initial_sweep_angle + delta.abs() * sign, input, responses);
return;
}
// Clamp sweep angle above 360°, switch to start
if new_sweep_angle > 360. {
let delta = angle_delta - (360. - current_sweep_angle);
let sign = delta.signum() * -1.;
self.total_angle_delta = angle_delta;
self.initial_sweep_angle = 360.;
self.endpoint = EndpointType::Start;
self.apply_arc_update(node_id, self.initial_start_angle + angle_delta, self.initial_sweep_angle + angle_delta.abs() * sign, input, responses);
return;
}
if let Some(snapped_delta) = self.check_snapping(self.initial_start_angle, self.initial_sweep_angle + angle) {
total += snapped_delta;
self.update_state(SweepAngleGizmoState::Snapped);
}
self.total_angle_delta = angle;
self.apply_arc_update(node_id, self.initial_start_angle, self.initial_sweep_angle + total, input, responses);
}
EndpointType::None => {}
} }
}
/// Applies the updated start and sweep angles to the arc.
fn apply_arc_update(&mut self, node_id: NodeId, start_angle: f64, sweep_angle: f64, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>) {
self.snap_angles = self.calculate_snap_angles(start_angle, sweep_angle);
responses.add(NodeGraphMessage::SetInput {
input_connector: InputConnector::node(node_id, 2),
input: NodeInput::value(TaggedValue::F64(start_angle), false),
});
responses.add(NodeGraphMessage::SetInput {
input_connector: InputConnector::node(node_id, 3),
input: NodeInput::value(TaggedValue::F64(sweep_angle), false),
});
self.previous_mouse_position = input.mouse.position; self.previous_mouse_position = input.mouse.position;
self.total_angle_delta = angle;
responses.add(NodeGraphMessage::RunDocumentGraph); responses.add(NodeGraphMessage::RunDocumentGraph);
} }
@ -299,7 +411,7 @@ impl SweepAngleGizmo {
if self.endpoint == EndpointType::End { if self.endpoint == EndpointType::End {
for i in 0..8 { for i in 0..8 {
let snap_point = i as f64 * FRAC_PI_4; let snap_point = wrap_to_tau(i as f64 * FRAC_PI_4 + initial_start_angle);
snap_points.push(snap_point.to_degrees()); snap_points.push(snap_point.to_degrees());
} }
} }

View file

@ -1,7 +1,7 @@
use super::misc::{ArcType, AsU64, GridType}; use super::misc::{ArcType, AsU64, GridType};
use super::{PointId, SegmentId, StrokeId}; use super::{PointId, SegmentId, StrokeId};
use crate::Ctx; use crate::Ctx;
use crate::registry::types::PixelSize; use crate::registry::types::{Angle, PixelSize};
use crate::vector::{HandleId, VectorData, VectorDataTable}; use crate::vector::{HandleId, VectorData, VectorDataTable};
use bezier_rs::Subpath; use bezier_rs::Subpath;
use glam::DVec2; use glam::DVec2;