Refactor collection of snap targets (#2114)

* Collect snap targets cleanup

* Make Clippy happy

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
James Lindsay 2024-11-21 06:27:49 +00:00 committed by GitHub
parent d7a271f675
commit 51ce51ea8c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 133 additions and 83 deletions

View File

@ -30,6 +30,9 @@ pub const DRAG_BEYOND_VIEWPORT_SPEED_FACTOR: f64 = 20.;
// Snapping point
pub const SNAP_POINT_TOLERANCE: f64 = 5.;
pub const MAX_ALIGNMENT_CANDIDATES: usize = 100; // These are layers whose bounding boxes are used for alignment.
pub const MAX_SNAP_CANDIDATES: usize = 10; // These are layers that are used for the layer snapper
pub const MAX_LAYER_SNAP_POINTS: usize = 100; // These are points (anchors and bounding box corners etc.) in the layer snapper
pub const DRAG_THRESHOLD: f64 = 1.;

View File

@ -5,7 +5,7 @@ use crate::messages::tool::common_functionality::snapping::SnapManager;
use crate::messages::{input_mapper::utility_types::input_keyboard::Key, portfolio::document::graph_operation::utility_types::TransformIn};
use glam::{DAffine2, DVec2, Vec2Swizzles};
use super::snapping::{SnapCandidatePoint, SnapConstraint, SnapData};
use super::snapping::{SnapCandidatePoint, SnapConstraint, SnapData, SnapTypeConfiguration};
#[derive(Clone, Debug, Default)]
pub struct Resize {
@ -19,7 +19,7 @@ impl Resize {
pub fn start(&mut self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler) {
let root_transform = document.metadata().document_to_viewport;
let point = SnapCandidatePoint::handle(root_transform.inverse().transform_point2(input.mouse.position));
let snapped = self.snap_manager.free_snap(&SnapData::new(document, input), &point, None, false);
let snapped = self.snap_manager.free_snap(&SnapData::new(document, input), &point, SnapTypeConfiguration::default());
self.drag_start = snapped.snapped_point_document;
}
@ -51,6 +51,7 @@ impl Resize {
let ratio = input.keyboard.get(lock_ratio as usize);
let center = input.keyboard.get(center as usize);
let snap_data = SnapData::ignore(document, input, &ignore);
let config = SnapTypeConfiguration::default();
if ratio {
let size = points_viewport[1] - points_viewport[0];
let size = size.abs().max(size.abs().yx()) * size.signum();
@ -61,27 +62,28 @@ impl Resize {
direction: end_document - self.drag_start,
};
if center {
let snapped = self.snap_manager.constrained_snap(&snap_data, &SnapCandidatePoint::handle(end_document), constraint, None);
let snapped = self.snap_manager.constrained_snap(&snap_data, &SnapCandidatePoint::handle(end_document), constraint, config);
let far = SnapCandidatePoint::handle(2. * self.drag_start - end_document);
let snapped_far = self.snap_manager.constrained_snap(&snap_data, &far, constraint, None);
let snapped_far = self.snap_manager.constrained_snap(&snap_data, &far, constraint, config);
let best = if snapped_far.other_snap_better(&snapped) { snapped } else { snapped_far };
points_viewport[0] = document_to_viewport.transform_point2(best.snapped_point_document);
points_viewport[1] = document_to_viewport.transform_point2(self.drag_start * 2. - best.snapped_point_document);
self.snap_manager.update_indicator(best);
} else {
let snapped = self.snap_manager.constrained_snap(&snap_data, &SnapCandidatePoint::handle(end_document), constraint, None);
let snapped = self.snap_manager.constrained_snap(&snap_data, &SnapCandidatePoint::handle(end_document), constraint, config);
points_viewport[1] = document_to_viewport.transform_point2(snapped.snapped_point_document);
self.snap_manager.update_indicator(snapped);
}
} else if center {
let snapped = self.snap_manager.free_snap(&snap_data, &SnapCandidatePoint::handle(document_mouse), None, false);
let snapped_far = self.snap_manager.free_snap(&snap_data, &SnapCandidatePoint::handle(2. * self.drag_start - document_mouse), None, false);
let snapped = self.snap_manager.free_snap(&snap_data, &SnapCandidatePoint::handle(document_mouse), config);
let opposite = 2. * self.drag_start - document_mouse;
let snapped_far = self.snap_manager.free_snap(&snap_data, &SnapCandidatePoint::handle(opposite), config);
let best = if snapped_far.other_snap_better(&snapped) { snapped } else { snapped_far };
points_viewport[0] = document_to_viewport.transform_point2(best.snapped_point_document);
points_viewport[1] = document_to_viewport.transform_point2(self.drag_start * 2. - best.snapped_point_document);
self.snap_manager.update_indicator(best);
} else {
let snapped = self.snap_manager.free_snap(&snap_data, &SnapCandidatePoint::handle(document_mouse), None, false);
let snapped = self.snap_manager.free_snap(&snap_data, &SnapCandidatePoint::handle(document_mouse), config);
points_viewport[1] = document_to_viewport.transform_point2(snapped.snapped_point_document);
self.snap_manager.update_indicator(snapped);
}

View File

@ -4,6 +4,7 @@ use crate::messages::portfolio::document::utility_types::document_metadata::{Doc
use crate::messages::portfolio::document::utility_types::misc::{GeometrySnapSource, SnapSource};
use crate::messages::portfolio::document::utility_types::network_interface::NodeNetworkInterface;
use crate::messages::prelude::*;
use crate::messages::tool::common_functionality::snapping::SnapTypeConfiguration;
use crate::messages::tool::tool_messages::path_tool::PointSelectState;
use bezier_rs::{Bezier, BezierHandles, TValue};
@ -210,7 +211,7 @@ impl ShapeState {
}
}
let snapped = snap_manager.free_snap(&snap_data, &point, None, false);
let snapped = snap_manager.free_snap(&snap_data, &point, SnapTypeConfiguration::default());
if best_snapped.other_snap_better(&snapped) {
offset = snapped.snapped_point_document - point.document_point + mouse_delta;
best_snapped = snapped;

View File

@ -20,6 +20,15 @@ use glam::{DAffine2, DVec2};
use graphene_std::vector::NoHashBuilder;
use std::cmp::Ordering;
/// Configuration for the relevant snap type
#[derive(Debug, Clone, Copy, Default)]
pub struct SnapTypeConfiguration {
pub only_geometry: bool,
pub use_existing_candidates: bool,
pub accept_distribution: bool,
pub bbox: Option<Rect>,
}
/// Handles snapping and snap overlays
#[derive(Debug, Clone, Default)]
pub struct SnapManager {
@ -242,7 +251,7 @@ impl SnapManager {
}
pub fn preview_draw(&mut self, snap_data: &SnapData, mouse: DVec2) {
let point = SnapCandidatePoint::handle(snap_data.document.metadata().document_to_viewport.inverse().transform_point2(mouse));
let snapped = self.free_snap(snap_data, &point, None, false);
let snapped = self.free_snap(snap_data, &point, SnapTypeConfiguration::default());
self.update_indicator(snapped);
}
@ -342,64 +351,64 @@ impl SnapManager {
self.add_candidates(layer, snap_data, quad);
}
if self.alignment_candidates.as_ref().is_some_and(|candidates| candidates.len() > 100) {
if self.alignment_candidates.as_ref().is_some_and(|candidates| candidates.len() > crate::consts::MAX_ALIGNMENT_CANDIDATES) {
warn!("Alignment candidate overflow");
}
if self.candidates.as_ref().is_some_and(|candidates| candidates.len() > 10) {
if self.candidates.as_ref().is_some_and(|candidates| candidates.len() > crate::consts::MAX_SNAP_CANDIDATES) {
warn!("Snap candidate overflow");
}
}
pub fn free_snap(&mut self, snap_data: &SnapData, point: &SnapCandidatePoint, bbox: Option<Rect>, to_paths: bool) -> SnappedPoint {
pub fn free_snap(&mut self, snap_data: &SnapData, point: &SnapCandidatePoint, config: SnapTypeConfiguration) -> SnappedPoint {
if !point.document_point.is_finite() {
warn!("Snapping non-finite position");
return SnappedPoint::infinite_snap(DVec2::ZERO);
}
let mut snap_results = SnapResults::default();
if point.source_index == 0 {
if !config.use_existing_candidates {
self.candidates = None;
}
let mut snap_data = snap_data.clone();
if snap_data.candidates.is_none() {
self.find_candidates(&snap_data, point, bbox);
self.find_candidates(&snap_data, point, config.bbox);
}
snap_data.candidates = self.candidates.as_ref();
snap_data.alignment_candidates = self.alignment_candidates.as_ref();
self.layer_snapper.free_snap(&mut snap_data, point, &mut snap_results);
self.layer_snapper.free_snap(&mut snap_data, point, &mut snap_results, config);
self.grid_snapper.free_snap(&mut snap_data, point, &mut snap_results);
self.alignment_snapper.free_snap(&mut snap_data, point, &mut snap_results);
self.distribution_snapper.free_snap(&mut snap_data, point, &mut snap_results, bbox);
self.alignment_snapper.free_snap(&mut snap_data, point, &mut snap_results, config);
self.distribution_snapper.free_snap(&mut snap_data, point, &mut snap_results, config);
Self::find_best_snap(&mut snap_data, point, snap_results, false, false, to_paths)
Self::find_best_snap(&mut snap_data, point, snap_results, false, false, config.only_geometry)
}
pub fn constrained_snap(&mut self, snap_data: &SnapData, point: &SnapCandidatePoint, constraint: SnapConstraint, bbox: Option<Rect>) -> SnappedPoint {
pub fn constrained_snap(&mut self, snap_data: &SnapData, point: &SnapCandidatePoint, constraint: SnapConstraint, config: SnapTypeConfiguration) -> SnappedPoint {
if !point.document_point.is_finite() {
warn!("Snapping non-finite position");
return SnappedPoint::infinite_snap(DVec2::ZERO);
}
let mut snap_results = SnapResults::default();
if point.source_index == 0 {
if !config.use_existing_candidates {
self.candidates = None;
}
let mut snap_data = snap_data.clone();
if snap_data.candidates.is_none() {
self.find_candidates(&snap_data, point, bbox);
self.find_candidates(&snap_data, point, config.bbox);
}
snap_data.candidates = self.candidates.as_ref();
snap_data.alignment_candidates = self.alignment_candidates.as_ref();
self.layer_snapper.constrained_snap(&mut snap_data, point, &mut snap_results, constraint);
self.layer_snapper.constrained_snap(&mut snap_data, point, &mut snap_results, constraint, config);
self.grid_snapper.constrained_snap(&mut snap_data, point, &mut snap_results, constraint);
self.alignment_snapper.constrained_snap(&mut snap_data, point, &mut snap_results, constraint);
self.distribution_snapper.constrained_snap(&mut snap_data, point, &mut snap_results, constraint, bbox);
self.alignment_snapper.constrained_snap(&mut snap_data, point, &mut snap_results, constraint, config);
self.distribution_snapper.constrained_snap(&mut snap_data, point, &mut snap_results, constraint, config);
Self::find_best_snap(&mut snap_data, point, snap_results, true, false, false)
Self::find_best_snap(&mut snap_data, point, snap_results, true, false, config.only_geometry)
}
fn alignment_x_overlay(boxes: &VecDeque<Rect>, transform: DAffine2, overlay_context: &mut OverlayContext) {

View File

@ -50,8 +50,8 @@ impl AlignmentSnapper {
}
}
pub fn snap_bbox_points(&mut self, snap_data: &mut SnapData, point: &SnapCandidatePoint, snap_results: &mut SnapResults, constraint: SnapConstraint) {
self.collect_bounding_box_points(snap_data, point.source_index == 0);
pub fn snap_bbox_points(&mut self, snap_data: &mut SnapData, point: &SnapCandidatePoint, snap_results: &mut SnapResults, constraint: SnapConstraint, config: SnapTypeConfiguration) {
self.collect_bounding_box_points(snap_data, !config.use_existing_candidates);
let unselected_geometry = if snap_data.document.snapping_state.target_enabled(SnapTarget::Alignment(AlignmentSnapTarget::Handle)) {
snap_data.node_snap_cache.map(|cache| cache.unselected.as_slice()).unwrap_or(&[])
} else {
@ -154,23 +154,23 @@ impl AlignmentSnapper {
_ => {}
}
}
pub fn free_snap(&mut self, snap_data: &mut SnapData, point: &SnapCandidatePoint, snap_results: &mut SnapResults) {
pub fn free_snap(&mut self, snap_data: &mut SnapData, point: &SnapCandidatePoint, snap_results: &mut SnapResults, config: SnapTypeConfiguration) {
let is_bbox = matches!(point.source, SnapSource::BoundingBox(_));
let is_geometry = matches!(point.source, SnapSource::Geometry(_));
let geometry_selected = snap_data.has_manipulators();
if is_bbox || (is_geometry && geometry_selected) || (is_geometry && point.alignment) {
self.snap_bbox_points(snap_data, point, snap_results, SnapConstraint::None);
self.snap_bbox_points(snap_data, point, snap_results, SnapConstraint::None, config);
}
}
pub fn constrained_snap(&mut self, snap_data: &mut SnapData, point: &SnapCandidatePoint, snap_results: &mut SnapResults, constraint: SnapConstraint) {
pub fn constrained_snap(&mut self, snap_data: &mut SnapData, point: &SnapCandidatePoint, snap_results: &mut SnapResults, constraint: SnapConstraint, config: SnapTypeConfiguration) {
let is_bbox = matches!(point.source, SnapSource::BoundingBox(_));
let is_geometry = matches!(point.source, SnapSource::Geometry(_));
let geometry_selected = snap_data.has_manipulators();
if is_bbox || (is_geometry && geometry_selected) || (is_geometry && point.alignment) {
self.snap_bbox_points(snap_data, point, snap_results, constraint);
self.snap_bbox_points(snap_data, point, snap_results, constraint, config);
}
}
}

View File

@ -323,22 +323,23 @@ impl DistributionSnapper {
}
}
pub fn free_snap(&mut self, snap_data: &mut SnapData, point: &SnapCandidatePoint, snap_results: &mut SnapResults, bounds: Option<Rect>) {
let Some(bounds) = bounds else { return };
pub fn free_snap(&mut self, snap_data: &mut SnapData, point: &SnapCandidatePoint, snap_results: &mut SnapResults, config: SnapTypeConfiguration) {
let Some(bounds) = config.bbox else { return };
if point.source != SnapSource::BoundingBox(BoundingBoxSnapSource::Center) || !snap_data.document.snapping_state.bounds.distribute {
return;
}
self.collect_bounding_box_points(snap_data, point.source_index == 0, bounds);
self.collect_bounding_box_points(snap_data, config.accept_distribution, bounds);
self.snap_bbox_points(snap_tolerance(snap_data.document), point, snap_results, SnapConstraint::None, bounds);
}
pub fn constrained_snap(&mut self, snap_data: &mut SnapData, point: &SnapCandidatePoint, snap_results: &mut SnapResults, constraint: SnapConstraint, bounds: Option<Rect>) {
let Some(bounds) = bounds else { return };
pub fn constrained_snap(&mut self, snap_data: &mut SnapData, point: &SnapCandidatePoint, snap_results: &mut SnapResults, constraint: SnapConstraint, config: SnapTypeConfiguration) {
let Some(bounds) = config.bbox else { return };
if point.source != SnapSource::BoundingBox(BoundingBoxSnapSource::Center) || !snap_data.document.snapping_state.bounds.distribute {
return;
}
self.collect_bounding_box_points(snap_data, point.source_index == 0, bounds);
self.collect_bounding_box_points(snap_data, config.accept_distribution, bounds);
self.snap_bbox_points(snap_tolerance(snap_data.document), point, snap_results, constraint, bounds);
}
}

View File

@ -92,8 +92,8 @@ impl LayerSnapper {
}
}
}
pub fn free_snap_paths(&mut self, snap_data: &mut SnapData, point: &SnapCandidatePoint, snap_results: &mut SnapResults) {
self.collect_paths(snap_data, point.source_index == 0);
pub fn free_snap_paths(&mut self, snap_data: &mut SnapData, point: &SnapCandidatePoint, snap_results: &mut SnapResults, config: SnapTypeConfiguration) {
self.collect_paths(snap_data, !config.use_existing_candidates);
let document = snap_data.document;
let normals = document.snapping_state.target_enabled(SnapTarget::Geometry(GeometrySnapTarget::Normal));
@ -131,9 +131,9 @@ impl LayerSnapper {
}
}
pub fn snap_paths_constrained(&mut self, snap_data: &mut SnapData, point: &SnapCandidatePoint, snap_results: &mut SnapResults, constraint: SnapConstraint) {
pub fn snap_paths_constrained(&mut self, snap_data: &mut SnapData, point: &SnapCandidatePoint, snap_results: &mut SnapResults, constraint: SnapConstraint, config: SnapTypeConfiguration) {
let document = snap_data.document;
self.collect_paths(snap_data, point.source_index == 0);
self.collect_paths(snap_data, !config.use_existing_candidates);
let tolerance = snap_tolerance(document);
let constraint_path = if let SnapConstraint::Circle { center, radius } = constraint {
@ -182,6 +182,10 @@ impl LayerSnapper {
if !document.network_interface.is_artboard(&layer.to_node(), &[]) || snap_data.ignore.contains(&layer) {
continue;
}
if self.points_to_snap.len() >= crate::consts::MAX_LAYER_SNAP_POINTS {
warn!("Snap point overflow; skipping.");
return;
}
if document.snapping_state.target_enabled(SnapTarget::Artboard(ArtboardSnapTarget::Corner)) {
let Some(bounds) = document
@ -201,6 +205,10 @@ impl LayerSnapper {
if snap_data.ignore_bounds(layer) {
continue;
}
if self.points_to_snap.len() >= crate::consts::MAX_LAYER_SNAP_POINTS {
warn!("Snap point overflow; skipping.");
return;
}
let Some(bounds) = document.metadata().bounding_box_with_transform(layer, DAffine2::IDENTITY) else {
continue;
};
@ -210,7 +218,6 @@ impl LayerSnapper {
}
}
pub fn snap_anchors(&mut self, snap_data: &mut SnapData, point: &SnapCandidatePoint, snap_results: &mut SnapResults, c: SnapConstraint, constrained_point: DVec2) {
self.collect_anchors(snap_data, point.source_index == 0);
let mut best = None;
for candidate in &self.points_to_snap {
// Candidate is not on constraint
@ -244,14 +251,16 @@ impl LayerSnapper {
snap_results.points.push(result);
}
}
pub fn free_snap(&mut self, snap_data: &mut SnapData, point: &SnapCandidatePoint, snap_results: &mut SnapResults) {
pub fn free_snap(&mut self, snap_data: &mut SnapData, point: &SnapCandidatePoint, snap_results: &mut SnapResults, config: SnapTypeConfiguration) {
self.collect_anchors(snap_data, !config.use_existing_candidates);
self.snap_anchors(snap_data, point, snap_results, SnapConstraint::None, point.document_point);
self.free_snap_paths(snap_data, point, snap_results);
self.free_snap_paths(snap_data, point, snap_results, config);
}
pub fn constrained_snap(&mut self, snap_data: &mut SnapData, point: &SnapCandidatePoint, snap_results: &mut SnapResults, constraint: SnapConstraint) {
pub fn constrained_snap(&mut self, snap_data: &mut SnapData, point: &SnapCandidatePoint, snap_results: &mut SnapResults, constraint: SnapConstraint, config: SnapTypeConfiguration) {
self.collect_anchors(snap_data, !config.use_existing_candidates);
self.snap_anchors(snap_data, point, snap_results, constraint, constraint.projection(point.document_point));
self.snap_paths_constrained(snap_data, point, snap_results, constraint);
self.snap_paths_constrained(snap_data, point, snap_results, constraint, config);
}
}
@ -313,7 +322,6 @@ pub struct SnapCandidatePoint {
pub document_point: DVec2,
pub source: SnapSource,
pub target: SnapTarget,
pub source_index: usize,
pub quad: Option<Quad>,
pub neighbors: Vec<DVec2>,
pub alignment: bool,
@ -417,6 +425,9 @@ fn subpath_anchor_snap_points(layer: LayerNodeIdentifier, subpath: &Subpath<Poin
if snap_data.ignore_manipulator(layer, subpath.manipulator_groups()[index].id) || snap_data.ignore_manipulator(layer, subpath.manipulator_groups()[(index + 1) % subpath.len()].id) {
continue;
}
if points.len() >= crate::consts::MAX_LAYER_SNAP_POINTS {
return;
}
let in_handle = curve.handle_start().map(|handle| handle - curve.start).filter(handle_not_under(to_document));
let out_handle = curve.handle_end().map(|handle| handle - curve.end).filter(handle_not_under(to_document));
@ -435,6 +446,10 @@ fn subpath_anchor_snap_points(layer: LayerNodeIdentifier, subpath: &Subpath<Poin
continue;
}
if points.len() >= crate::consts::MAX_LAYER_SNAP_POINTS {
return;
}
let colinear = are_manipulator_handles_colinear(group, to_document, subpath, index);
if colinear && document.snapping_state.target_enabled(SnapTarget::Geometry(GeometrySnapTarget::AnchorWithColinearHandles)) {
@ -471,6 +486,9 @@ pub fn get_layer_snap_points(layer: LayerNodeIdentifier, snap_data: &SnapData, p
if document.network_interface.is_artboard(&layer.to_node(), &[]) {
return;
}
if points.len() >= crate::consts::MAX_LAYER_SNAP_POINTS {
return;
}
if layer.has_children(document.metadata()) {
for child in layer.descendants(document.metadata()) {

View File

@ -3,6 +3,7 @@ use crate::messages::frontend::utility_types::MouseCursorIcon;
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
use crate::messages::portfolio::document::utility_types::transformation::OriginalTransforms;
use crate::messages::prelude::*;
use crate::messages::tool::common_functionality::snapping::SnapTypeConfiguration;
use graphene_core::renderer::Quad;
@ -124,7 +125,15 @@ impl SelectedEdges {
let mut best_snap = SnappedPoint::infinite_snap(pivot);
let mut best_scale_factor = DVec2::ONE;
let tolerance = snapping::snap_tolerance(snap_data.document);
for point in points {
let bbox = Some(Rect::from_box((bounds_to_doc * Quad::from_box([min, max])).bounding_box()));
for (index, point) in points.iter_mut().enumerate() {
let config = SnapTypeConfiguration {
bbox,
use_existing_candidates: index != 0,
..Default::default()
};
let old_position = point.document_point;
let bounds_space = bounds_to_doc.inverse().transform_point2(point.document_point);
let normalized = (bounds_space - self.bounds[0]) / (self.bounds[1] - self.bounds[0]);
@ -135,16 +144,16 @@ impl SelectedEdges {
origin: point.document_point,
direction: (point.document_point - bounds_to_doc.transform_point2(pivot)).normalize_or_zero(),
};
manager.constrained_snap(&snap_data, point, constraint, None)
manager.constrained_snap(&snap_data, point, constraint, config)
} else if !(self.top || self.bottom) || !(self.left || self.right) {
let axis = if !(self.top || self.bottom) { DVec2::X } else { DVec2::Y };
let constraint = SnapConstraint::Line {
origin: point.document_point,
direction: bounds_to_doc.transform_vector2(axis),
};
manager.constrained_snap(&snap_data, point, constraint, None)
manager.constrained_snap(&snap_data, point, constraint, config)
} else {
manager.free_snap(&snap_data, point, None, false)
manager.free_snap(&snap_data, point, config)
};
point.document_point = old_position;
@ -218,7 +227,7 @@ pub fn axis_align_drag(axis_align: bool, position: DVec2, start: DVec2) -> DVec2
}
/// Snaps a dragging event from the artboard or select tool
pub fn snap_drag(start: DVec2, current: DVec2, axis_align: bool, snap_data: SnapData, snap_manager: &mut SnapManager, candidates: &Vec<SnapCandidatePoint>) -> DVec2 {
pub fn snap_drag(start: DVec2, current: DVec2, axis_align: bool, snap_data: SnapData, snap_manager: &mut SnapManager, candidates: &[SnapCandidatePoint]) -> DVec2 {
let mouse_position = axis_align_drag(axis_align, snap_data.input.mouse.position, start);
let document = snap_data.document;
let total_mouse_delta_document = document.metadata().document_to_viewport.inverse().transform_vector2(mouse_position - start);
@ -228,22 +237,25 @@ pub fn snap_drag(start: DVec2, current: DVec2, axis_align: bool, snap_data: Snap
let bbox = Rect::point_iter(candidates.iter().map(|candidate| candidate.document_point + total_mouse_delta_document));
for point in candidates {
for (index, point) in candidates.iter().enumerate() {
let config = SnapTypeConfiguration {
bbox,
accept_distribution: true,
use_existing_candidates: index != 0,
..Default::default()
};
let mut point = point.clone();
point.document_point += total_mouse_delta_document;
let snapped = if axis_align {
snap_manager.constrained_snap(
&snap_data,
&point,
SnapConstraint::Line {
let constraint = SnapConstraint::Line {
origin: point.document_point,
direction: total_mouse_delta_document.try_normalize().unwrap_or(DVec2::X),
},
bbox,
)
};
snap_manager.constrained_snap(&snap_data, &point, constraint, config)
} else {
snap_manager.free_snap(&snap_data, &point, bbox, false)
snap_manager.free_snap(&snap_data, &point, config)
};
if best_snap.other_snap_better(&snapped) {

View File

@ -6,6 +6,7 @@ use crate::messages::tool::common_functionality::snapping;
use crate::messages::tool::common_functionality::snapping::SnapCandidatePoint;
use crate::messages::tool::common_functionality::snapping::SnapData;
use crate::messages::tool::common_functionality::snapping::SnapManager;
use crate::messages::tool::common_functionality::snapping::SnapTypeConfiguration;
use crate::messages::tool::common_functionality::transformation_cage::*;
use graph_craft::document::NodeId;
@ -260,7 +261,7 @@ impl Fsm for ArtboardToolFsmState {
let point = SnapCandidatePoint::handle(to_document.transform_point2(input.mouse.position));
let snapped = tool_data.snap_manager.free_snap(&SnapData::new(document, input), &point, None, false);
let snapped = tool_data.snap_manager.free_snap(&SnapData::new(document, input), &point, SnapTypeConfiguration::default());
tool_data.drag_start = snapped.snapped_point_document;
tool_data.drag_current = snapped.snapped_point_document;
@ -330,7 +331,8 @@ impl Fsm for ArtboardToolFsmState {
let document_mouse = to_viewport.inverse().transform_point2(input.mouse.position);
let snapped = tool_data.snap_manager.free_snap(&snap_data, &SnapCandidatePoint::handle(document_mouse), None, false);
let config = SnapTypeConfiguration::default();
let snapped = tool_data.snap_manager.free_snap(&snap_data, &SnapCandidatePoint::handle(document_mouse), config);
let snapped_mouse_position = to_viewport.transform_point2(snapped.snapped_point_document);
tool_data.snap_manager.update_indicator(snapped);

View File

@ -7,7 +7,7 @@ use crate::messages::portfolio::document::utility_types::document_metadata::Laye
use crate::messages::tool::common_functionality::auto_panning::AutoPanning;
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType};
use crate::messages::tool::common_functionality::graph_modification_utils;
use crate::messages::tool::common_functionality::snapping::{SnapCandidatePoint, SnapConstraint, SnapData, SnapManager};
use crate::messages::tool::common_functionality::snapping::{SnapCandidatePoint, SnapConstraint, SnapData, SnapManager, SnapTypeConfiguration};
use graph_craft::document::{value::TaggedValue, NodeId, NodeInput};
use graphene_core::Color;
@ -173,7 +173,7 @@ impl Fsm for LineToolFsmState {
}
(LineToolFsmState::Ready, LineToolMessage::DragStart) => {
let point = SnapCandidatePoint::handle(document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position));
let snapped = tool_data.snap_manager.free_snap(&SnapData::new(document, input), &point, None, false);
let snapped = tool_data.snap_manager.free_snap(&SnapData::new(document, input), &point, SnapTypeConfiguration::default());
tool_data.drag_start = snapped.snapped_point_document;
responses.add(DocumentMessage::StartTransaction);
@ -314,6 +314,7 @@ fn generate_transform(tool_data: &mut LineToolData, snap_data: SnapData, lock_an
let near_point = SnapCandidatePoint::handle_neighbors(document_points[1], [tool_data.drag_start]);
let far_point = SnapCandidatePoint::handle_neighbors(2. * document_points[0] - document_points[1], [tool_data.drag_start]);
let config = SnapTypeConfiguration::default();
if constrained {
let constraint = SnapConstraint::Line {
@ -321,26 +322,26 @@ fn generate_transform(tool_data: &mut LineToolData, snap_data: SnapData, lock_an
direction: document_points[1] - document_points[0],
};
if center {
let snapped = snap.constrained_snap(&snap_data, &near_point, constraint, None);
let snapped_far = snap.constrained_snap(&snap_data, &far_point, constraint, None);
let snapped = snap.constrained_snap(&snap_data, &near_point, constraint, config);
let snapped_far = snap.constrained_snap(&snap_data, &far_point, constraint, config);
let best = if snapped_far.other_snap_better(&snapped) { snapped } else { snapped_far };
document_points[1] = document_points[0] * 2. - best.snapped_point_document;
document_points[0] = best.snapped_point_document;
snap.update_indicator(best);
} else {
let snapped = snap.constrained_snap(&snap_data, &near_point, constraint, None);
let snapped = snap.constrained_snap(&snap_data, &near_point, constraint, config);
document_points[1] = snapped.snapped_point_document;
snap.update_indicator(snapped);
}
} else if center {
let snapped = snap.free_snap(&snap_data, &near_point, None, false);
let snapped_far = snap.free_snap(&snap_data, &far_point, None, false);
let snapped = snap.free_snap(&snap_data, &near_point, config);
let snapped_far = snap.free_snap(&snap_data, &far_point, config);
let best = if snapped_far.other_snap_better(&snapped) { snapped } else { snapped_far };
document_points[1] = document_points[0] * 2. - best.snapped_point_document;
document_points[0] = best.snapped_point_document;
snap.update_indicator(best);
} else {
let snapped = snap.free_snap(&snap_data, &near_point, None, false);
let snapped = snap.free_snap(&snap_data, &near_point, config);
document_points[1] = snapped.snapped_point_document;
snap.update_indicator(snapped);
}

View File

@ -8,7 +8,7 @@ use crate::messages::portfolio::document::utility_types::network_interface::Inpu
use crate::messages::tool::common_functionality::auto_panning::AutoPanning;
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType};
use crate::messages::tool::common_functionality::graph_modification_utils;
use crate::messages::tool::common_functionality::snapping::{SnapCandidatePoint, SnapConstraint, SnapData, SnapManager};
use crate::messages::tool::common_functionality::snapping::{SnapCandidatePoint, SnapConstraint, SnapData, SnapManager, SnapTypeConfiguration};
use crate::messages::tool::common_functionality::utility_functions::should_extend;
use bezier_rs::{Bezier, BezierHandles};
@ -395,6 +395,7 @@ impl PenToolData {
let neighbors = relative.filter(|_| neighbor).map_or(Vec::new(), |neighbor| vec![neighbor]);
let config = SnapTypeConfiguration::default();
if let Some(relative) = relative
.map(|layer| transform.transform_point2(layer))
.filter(|&relative| (snap_angle || lock_angle) && (relative - document_pos).length_squared() > f64::EPSILON * 100.)
@ -417,8 +418,8 @@ impl PenToolData {
let near_point = SnapCandidatePoint::handle_neighbors(document_pos, neighbors.clone());
let far_point = SnapCandidatePoint::handle_neighbors(2. * relative - document_pos, neighbors);
if colinear {
let snapped = snap.constrained_snap(&snap_data, &near_point, constraint, None);
let snapped_far = snap.constrained_snap(&snap_data, &far_point, constraint, None);
let snapped = snap.constrained_snap(&snap_data, &near_point, constraint, config);
let snapped_far = snap.constrained_snap(&snap_data, &far_point, constraint, config);
document_pos = if snapped_far.other_snap_better(&snapped) {
snapped.snapped_point_document
} else {
@ -426,13 +427,13 @@ impl PenToolData {
};
snap.update_indicator(if snapped_far.other_snap_better(&snapped) { snapped } else { snapped_far });
} else {
let snapped = snap.constrained_snap(&snap_data, &near_point, constraint, None);
let snapped = snap.constrained_snap(&snap_data, &near_point, constraint, config);
document_pos = snapped.snapped_point_document;
snap.update_indicator(snapped);
}
} else if let Some(relative) = relative.map(|layer| transform.transform_point2(layer)).filter(|_| colinear) {
let snapped = snap.free_snap(&snap_data, &SnapCandidatePoint::handle_neighbors(document_pos, neighbors.clone()), None, false);
let snapped_far = snap.free_snap(&snap_data, &SnapCandidatePoint::handle_neighbors(2. * relative - document_pos, neighbors), None, false);
let snapped = snap.free_snap(&snap_data, &SnapCandidatePoint::handle_neighbors(document_pos, neighbors.clone()), config);
let snapped_far = snap.free_snap(&snap_data, &SnapCandidatePoint::handle_neighbors(2. * relative - document_pos, neighbors), config);
document_pos = if snapped_far.other_snap_better(&snapped) {
snapped.snapped_point_document
} else {
@ -440,7 +441,7 @@ impl PenToolData {
};
snap.update_indicator(if snapped_far.other_snap_better(&snapped) { snapped } else { snapped_far });
} else {
let snapped = snap.free_snap(&snap_data, &SnapCandidatePoint::handle_neighbors(document_pos, neighbors), None, false);
let snapped = snap.free_snap(&snap_data, &SnapCandidatePoint::handle_neighbors(document_pos, neighbors), config);
document_pos = snapped.snapped_point_document;
snap.update_indicator(snapped);
}
@ -456,7 +457,7 @@ impl PenToolData {
fn create_initial_point(&mut self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler, responses: &mut VecDeque<Message>, tool_options: &PenOptions, append: bool) {
let point = SnapCandidatePoint::handle(document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position));
let snapped = self.snap_manager.free_snap(&SnapData::new(document, input), &point, None, false);
let snapped = self.snap_manager.free_snap(&SnapData::new(document, input), &point, SnapTypeConfiguration::default());
let viewport = document.metadata().document_to_viewport.transform_point2(snapped.snapped_point_document);
let selected_nodes = document.network_interface.selected_nodes(&[]).unwrap();
@ -632,7 +633,7 @@ impl Fsm for PenToolFsmState {
}
(PenToolFsmState::PlacingAnchor, PenToolMessage::DragStart { append_to_selected }) => {
let point = SnapCandidatePoint::handle(document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position));
let snapped = tool_data.snap_manager.free_snap(&SnapData::new(document, input), &point, None, false);
let snapped = tool_data.snap_manager.free_snap(&SnapData::new(document, input), &point, SnapTypeConfiguration::default());
let viewport = document.metadata().document_to_viewport.transform_point2(snapped.snapped_point_document);
// Early return if the buffer was started and this message is being run again after the buffer (so that place_anchor updates the state with the newly merged vector)
if tool_data.buffering_merged_vector {