mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-07-08 00:05:00 +00:00
fixed wrapping need to fix snapping and overlays
This commit is contained in:
parent
cdb1bf7707
commit
0b548e5281
2 changed files with 146 additions and 34 deletions
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue