mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-25 10:58:11 +08:00
Improve snapping with better snap target names, tooltips, cleaner overlay labels, code cleanup
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
use super::graph_modification_utils;
|
||||
use super::snapping::{SnapCache, SnapCandidatePoint, SnapData, SnapManager, SnappedPoint};
|
||||
use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
|
||||
use crate::messages::portfolio::document::utility_types::misc::{GeometrySnapSource, SnapSource};
|
||||
use crate::messages::portfolio::document::utility_types::misc::{PathSnapSource, SnapSource};
|
||||
use crate::messages::portfolio::document::utility_types::network_interface::NodeNetworkInterface;
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::common_functionality::snapping::SnapTypeConfiguration;
|
||||
@@ -193,9 +193,10 @@ impl ShapeState {
|
||||
|
||||
for &selected in &state.selected_points {
|
||||
let source = match selected {
|
||||
ManipulatorPointId::Anchor(_) if vector_data.colinear(selected) => SnapSource::Geometry(GeometrySnapSource::AnchorWithColinearHandles),
|
||||
ManipulatorPointId::Anchor(_) => SnapSource::Geometry(GeometrySnapSource::AnchorWithFreeHandles),
|
||||
_ => SnapSource::Geometry(GeometrySnapSource::Handle),
|
||||
ManipulatorPointId::Anchor(_) if vector_data.colinear(selected) => SnapSource::Path(PathSnapSource::AnchorPointWithColinearHandles),
|
||||
ManipulatorPointId::Anchor(_) => SnapSource::Path(PathSnapSource::AnchorPointWithFreeHandles),
|
||||
// TODO: This doesn't actually work for handles, instead handles enter the arm above for free handles
|
||||
ManipulatorPointId::PrimaryHandle(_) | ManipulatorPointId::EndHandle(_) => SnapSource::Path(PathSnapSource::HandlePoint),
|
||||
};
|
||||
|
||||
let Some(position) = selected.get_position(&vector_data) else { continue };
|
||||
|
||||
@@ -8,7 +8,7 @@ pub use {alignment_snapper::*, distribution_snapper::*, grid_snapper::*, layer_s
|
||||
use crate::consts::{COLOR_OVERLAY_BLUE, COLOR_OVERLAY_SNAP_BACKGROUND, COLOR_OVERLAY_WHITE};
|
||||
use crate::messages::portfolio::document::overlays::utility_types::{OverlayContext, Pivot};
|
||||
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||
use crate::messages::portfolio::document::utility_types::misc::{BoundingBoxSnapTarget, GeometrySnapTarget, GridSnapTarget, SnapTarget};
|
||||
use crate::messages::portfolio::document::utility_types::misc::{BoundingBoxSnapTarget, GridSnapTarget, PathSnapTarget, SnapTarget};
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
use bezier_rs::{Subpath, TValue};
|
||||
@@ -23,7 +23,7 @@ use std::cmp::Ordering;
|
||||
/// Configuration for the relevant snap type
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct SnapTypeConfiguration {
|
||||
pub only_geometry: bool,
|
||||
pub only_path: bool,
|
||||
pub use_existing_candidates: bool,
|
||||
pub accept_distribution: bool,
|
||||
pub bbox: Option<Rect>,
|
||||
@@ -111,7 +111,7 @@ fn get_closest_point(points: Vec<SnappedPoint>) -> Option<SnappedPoint> {
|
||||
(None, None) => None,
|
||||
(Some(result), None) | (None, Some(result)) => Some(result),
|
||||
(Some(mut result), Some(align)) => {
|
||||
let SnapTarget::Distribution(distribution) = result.target else { return Some(result) };
|
||||
let SnapTarget::DistributeEvenly(distribution) = result.target else { return Some(result) };
|
||||
if distribution.is_x() && align.alignment_target_x.is_some() {
|
||||
result.snapped_point_document.y = align.snapped_point_document.y;
|
||||
result.alignment_target_x = align.alignment_target_x;
|
||||
@@ -126,7 +126,7 @@ fn get_closest_point(points: Vec<SnappedPoint>) -> Option<SnappedPoint> {
|
||||
}
|
||||
}
|
||||
fn get_closest_curve(curves: &[SnappedCurve], exclude_paths: bool) -> Option<&SnappedPoint> {
|
||||
let keep_curve = |curve: &&SnappedCurve| !exclude_paths || curve.point.target != SnapTarget::Geometry(GeometrySnapTarget::Path);
|
||||
let keep_curve = |curve: &&SnappedCurve| !exclude_paths || curve.point.target != SnapTarget::Path(PathSnapTarget::AlongPath);
|
||||
curves.iter().filter(keep_curve).map(|curve| &curve.point).min_by(compare_points)
|
||||
}
|
||||
fn get_closest_line(lines: &[SnappedLine]) -> Option<&SnappedPoint> {
|
||||
@@ -135,12 +135,12 @@ fn get_closest_line(lines: &[SnappedLine]) -> Option<&SnappedPoint> {
|
||||
fn get_closest_intersection(snap_to: DVec2, curves: &[SnappedCurve]) -> Option<SnappedPoint> {
|
||||
let mut best = None;
|
||||
for curve_i in curves {
|
||||
if curve_i.point.target == SnapTarget::BoundingBox(BoundingBoxSnapTarget::Edge) {
|
||||
if curve_i.point.target == SnapTarget::BoundingBox(BoundingBoxSnapTarget::AlongEdge) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for curve_j in curves {
|
||||
if curve_j.point.target == SnapTarget::BoundingBox(BoundingBoxSnapTarget::Edge) {
|
||||
if curve_j.point.target == SnapTarget::BoundingBox(BoundingBoxSnapTarget::AlongEdge) {
|
||||
continue;
|
||||
}
|
||||
if curve_i.start == curve_j.start && curve_i.layer == curve_j.layer {
|
||||
@@ -156,7 +156,7 @@ fn get_closest_intersection(snap_to: DVec2, curves: &[SnappedCurve]) -> Option<S
|
||||
best = Some(SnappedPoint {
|
||||
snapped_point_document,
|
||||
distance,
|
||||
target: SnapTarget::Geometry(GeometrySnapTarget::Intersection),
|
||||
target: SnapTarget::Path(PathSnapTarget::IntersectionPoint),
|
||||
tolerance: close.point.tolerance,
|
||||
curves: [Some(close.document_curve), Some(far.document_curve)],
|
||||
source: close.point.source,
|
||||
@@ -262,7 +262,7 @@ impl SnapManager {
|
||||
if let Some(closest_point) = get_closest_point(snap_results.points) {
|
||||
snapped_points.push(closest_point);
|
||||
}
|
||||
let exclude_paths = !document.snapping_state.target_enabled(SnapTarget::Geometry(GeometrySnapTarget::Path));
|
||||
let exclude_paths = !document.snapping_state.target_enabled(SnapTarget::Path(PathSnapTarget::AlongPath));
|
||||
if let Some(closest_curve) = get_closest_curve(&snap_results.curves, exclude_paths) {
|
||||
snapped_points.push(closest_curve.clone());
|
||||
}
|
||||
@@ -274,7 +274,7 @@ impl SnapManager {
|
||||
}
|
||||
|
||||
if !constrained {
|
||||
if document.snapping_state.target_enabled(SnapTarget::Geometry(GeometrySnapTarget::Intersection)) {
|
||||
if document.snapping_state.target_enabled(SnapTarget::Path(PathSnapTarget::IntersectionPoint)) {
|
||||
if let Some(closest_curves_intersection) = get_closest_intersection(point.document_point, &snap_results.curves) {
|
||||
snapped_points.push(closest_curves_intersection);
|
||||
}
|
||||
@@ -287,7 +287,7 @@ impl SnapManager {
|
||||
}
|
||||
|
||||
if to_path {
|
||||
snapped_points.retain(|i| matches!(i.target, SnapTarget::Geometry(_)));
|
||||
snapped_points.retain(|i| matches!(i.target, SnapTarget::Path(_)));
|
||||
}
|
||||
|
||||
let mut best_point = None;
|
||||
@@ -382,7 +382,7 @@ impl SnapManager {
|
||||
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, config.only_geometry)
|
||||
Self::find_best_snap(&mut snap_data, point, snap_results, false, false, config.only_path)
|
||||
}
|
||||
|
||||
pub fn constrained_snap(&mut self, snap_data: &SnapData, point: &SnapCandidatePoint, constraint: SnapConstraint, config: SnapTypeConfiguration) -> SnappedPoint {
|
||||
@@ -408,7 +408,7 @@ impl SnapManager {
|
||||
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, config.only_geometry)
|
||||
Self::find_best_snap(&mut snap_data, point, snap_results, true, false, config.only_path)
|
||||
}
|
||||
|
||||
fn alignment_x_overlay(boxes: &VecDeque<Rect>, transform: DAffine2, overlay_context: &mut OverlayContext) {
|
||||
@@ -475,9 +475,9 @@ impl SnapManager {
|
||||
}
|
||||
|
||||
if !any_align && ind.distribution_equal_distance_x.is_none() && ind.distribution_equal_distance_y.is_none() {
|
||||
let text = format!("{:?} to {:?}", ind.source, ind.target);
|
||||
let transform = DAffine2::from_translation(viewport - DVec2::new(0., 5.));
|
||||
overlay_context.text(&text, COLOR_OVERLAY_WHITE, Some(COLOR_OVERLAY_SNAP_BACKGROUND), transform, 5., [Pivot::Start, Pivot::End]);
|
||||
let text = format!("[{}] from [{}]", ind.target, ind.source);
|
||||
let transform = DAffine2::from_translation(viewport - DVec2::new(0., 4.));
|
||||
overlay_context.text(&text, COLOR_OVERLAY_WHITE, Some(COLOR_OVERLAY_SNAP_BACKGROUND), transform, 4., [Pivot::Start, Pivot::End]);
|
||||
overlay_context.square(viewport, Some(4.), Some(COLOR_OVERLAY_BLUE), Some(COLOR_OVERLAY_BLUE));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ impl AlignmentSnapper {
|
||||
let document = snap_data.document;
|
||||
|
||||
self.bounding_box_points.clear();
|
||||
if !document.snapping_state.bounds.align {
|
||||
if !document.snapping_state.bounding_box.align_with_corner_point {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ impl AlignmentSnapper {
|
||||
continue;
|
||||
}
|
||||
|
||||
if document.snapping_state.target_enabled(SnapTarget::Artboard(ArtboardSnapTarget::Corner)) {
|
||||
if document.snapping_state.target_enabled(SnapTarget::Artboard(ArtboardSnapTarget::CornerPoint)) {
|
||||
let Some(bounds) = document.metadata().bounding_box_with_transform(layer, document.metadata().transform_to_document(layer)) else {
|
||||
continue;
|
||||
};
|
||||
@@ -52,7 +52,7 @@ impl AlignmentSnapper {
|
||||
|
||||
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)) {
|
||||
let unselected_geometry = if snap_data.document.snapping_state.target_enabled(SnapTarget::Alignment(AlignmentSnapTarget::AlignWithAnchorPoint)) {
|
||||
snap_data.node_snap_cache.map(|cache| cache.unselected.as_slice()).unwrap_or(&[])
|
||||
} else {
|
||||
&[]
|
||||
@@ -77,9 +77,9 @@ impl AlignmentSnapper {
|
||||
[DVec2::new(point.document_point.x, target_position.y), DVec2::new(target_position.x, point.document_point.y)].map(Some)
|
||||
};
|
||||
|
||||
let target_geometry = matches!(target_point.target, SnapTarget::Geometry(_));
|
||||
let updated_target = if target_geometry {
|
||||
SnapTarget::Alignment(AlignmentSnapTarget::Handle)
|
||||
let target_path = matches!(target_point.target, SnapTarget::Path(_));
|
||||
let updated_target = if target_path {
|
||||
SnapTarget::Alignment(AlignmentSnapTarget::AlignWithAnchorPoint)
|
||||
} else {
|
||||
target_point.target
|
||||
};
|
||||
@@ -90,7 +90,7 @@ impl AlignmentSnapper {
|
||||
if distance_to_snapped < tolerance && snap_x.as_ref().map_or(true, |point| distance_to_align_target < point.distance_to_align_target) {
|
||||
snap_x = Some(SnappedPoint {
|
||||
snapped_point_document: point_on_x,
|
||||
source: point.source, //ToDo map source
|
||||
source: point.source, // TODO(0Hypercube): map source
|
||||
target: updated_target,
|
||||
target_bounds: target_point.quad,
|
||||
distance: distance_to_snapped,
|
||||
@@ -109,7 +109,7 @@ impl AlignmentSnapper {
|
||||
if distance_to_snapped < tolerance && snap_y.as_ref().map_or(true, |point| distance_to_align_target < point.distance_to_align_target) {
|
||||
snap_y = Some(SnappedPoint {
|
||||
snapped_point_document: point_on_y,
|
||||
source: point.source, //ToDo map source
|
||||
source: point.source, // TODO(0Hypercube): map source
|
||||
target: updated_target,
|
||||
target_bounds: target_point.quad,
|
||||
distance: distance_to_snapped,
|
||||
@@ -137,7 +137,7 @@ impl AlignmentSnapper {
|
||||
snap_results.points.push(SnappedPoint {
|
||||
snapped_point_document: intersection,
|
||||
source: point.source, // TODO: map source
|
||||
target: SnapTarget::Alignment(AlignmentSnapTarget::Intersection),
|
||||
target: SnapTarget::Alignment(AlignmentSnapTarget::IntersectionPoint),
|
||||
target_bounds: snap_x.target_bounds,
|
||||
distance,
|
||||
tolerance,
|
||||
@@ -154,22 +154,23 @@ impl AlignmentSnapper {
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
let is_path = matches!(point.source, SnapSource::Path(_));
|
||||
let path_selected = snap_data.has_manipulators();
|
||||
|
||||
if is_bbox || (is_geometry && geometry_selected) || (is_geometry && point.alignment) {
|
||||
if is_bbox || (is_path && path_selected) || (is_path && point.alignment) {
|
||||
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, 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();
|
||||
let is_path = matches!(point.source, SnapSource::Path(_));
|
||||
let path_selected = snap_data.has_manipulators();
|
||||
|
||||
if is_bbox || (is_geometry && geometry_selected) || (is_geometry && point.alignment) {
|
||||
if is_bbox || (is_path && path_selected) || (is_path && point.alignment) {
|
||||
self.snap_bbox_points(snap_data, point, snap_results, constraint, config);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,7 +210,7 @@ impl DistributionSnapper {
|
||||
let mut final_point = x;
|
||||
final_point.snapped_point_document += y.snapped_point_document - point.document_point;
|
||||
final_point.source_bounds = Some(final_bounds.into());
|
||||
final_point.target = SnapTarget::Distribution(DistributionSnapTarget::Xy);
|
||||
final_point.target = SnapTarget::DistributeEvenly(DistributionSnapTarget::XY);
|
||||
final_point.distribution_boxes_y = y.distribution_boxes_y;
|
||||
final_point.distribution_equal_distance_y = y.distribution_equal_distance_y;
|
||||
final_point.distance = (final_point.distance * final_point.distance + y.distance * y.distance).sqrt();
|
||||
@@ -325,7 +325,7 @@ impl DistributionSnapper {
|
||||
|
||||
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 {
|
||||
if point.source != SnapSource::BoundingBox(BoundingBoxSnapSource::CenterPoint) || !snap_data.document.snapping_state.bounding_box.distribute_evenly {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -335,7 +335,7 @@ impl DistributionSnapper {
|
||||
|
||||
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 {
|
||||
if point.source != SnapSource::BoundingBox(BoundingBoxSnapSource::CenterPoint) || !snap_data.document.snapping_state.bounding_box.distribute_evenly {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ pub struct LayerSnapper {
|
||||
points_to_snap: Vec<SnapCandidatePoint>,
|
||||
paths_to_snap: Vec<SnapCandidatePath>,
|
||||
}
|
||||
|
||||
impl LayerSnapper {
|
||||
pub fn add_layer_bounds(&mut self, document: &DocumentMessageHandler, layer: LayerNodeIdentifier, target: SnapTarget) {
|
||||
if !document.snapping_state.target_enabled(target) {
|
||||
@@ -49,6 +48,7 @@ impl LayerSnapper {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn collect_paths(&mut self, snap_data: &mut SnapData, first_point: bool) {
|
||||
if !first_point {
|
||||
return;
|
||||
@@ -60,7 +60,7 @@ impl LayerSnapper {
|
||||
if !document.network_interface.is_artboard(&layer.to_node(), &[]) || snap_data.ignore.contains(&layer) {
|
||||
continue;
|
||||
}
|
||||
self.add_layer_bounds(document, layer, SnapTarget::Artboard(ArtboardSnapTarget::Edge));
|
||||
self.add_layer_bounds(document, layer, SnapTarget::Artboard(ArtboardSnapTarget::AlongEdge));
|
||||
}
|
||||
for &layer in snap_data.get_candidates() {
|
||||
let transform = document.metadata().transform_to_document(layer);
|
||||
@@ -68,8 +68,7 @@ impl LayerSnapper {
|
||||
continue;
|
||||
}
|
||||
|
||||
if document.snapping_state.target_enabled(SnapTarget::Geometry(GeometrySnapTarget::Intersection)) || document.snapping_state.target_enabled(SnapTarget::Geometry(GeometrySnapTarget::Path))
|
||||
{
|
||||
if document.snapping_state.target_enabled(SnapTarget::Path(PathSnapTarget::IntersectionPoint)) || document.snapping_state.target_enabled(SnapTarget::Path(PathSnapTarget::AlongPath)) {
|
||||
for subpath in document.metadata().layer_outline(layer) {
|
||||
for (start_index, curve) in subpath.iter().enumerate() {
|
||||
let document_curve = curve.apply_transformation(|p| transform.transform_point2(p));
|
||||
@@ -81,23 +80,24 @@ impl LayerSnapper {
|
||||
document_curve,
|
||||
layer,
|
||||
start,
|
||||
target: SnapTarget::Geometry(GeometrySnapTarget::Path),
|
||||
target: SnapTarget::Path(PathSnapTarget::AlongPath),
|
||||
bounds: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
if !snap_data.ignore_bounds(layer) {
|
||||
self.add_layer_bounds(document, layer, SnapTarget::BoundingBox(BoundingBoxSnapTarget::Edge));
|
||||
self.add_layer_bounds(document, layer, SnapTarget::BoundingBox(BoundingBoxSnapTarget::AlongEdge));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
let tangents = document.snapping_state.target_enabled(SnapTarget::Geometry(GeometrySnapTarget::Tangent));
|
||||
let normals = document.snapping_state.target_enabled(SnapTarget::Path(PathSnapTarget::NormalToPath));
|
||||
let tangents = document.snapping_state.target_enabled(SnapTarget::Path(PathSnapTarget::TangentToPath));
|
||||
let tolerance = snap_tolerance(document);
|
||||
|
||||
for path in &self.paths_to_snap {
|
||||
@@ -187,7 +187,7 @@ impl LayerSnapper {
|
||||
return;
|
||||
}
|
||||
|
||||
if document.snapping_state.target_enabled(SnapTarget::Artboard(ArtboardSnapTarget::Corner)) {
|
||||
if document.snapping_state.target_enabled(SnapTarget::Artboard(ArtboardSnapTarget::CornerPoint)) {
|
||||
let Some(bounds) = document
|
||||
.network_interface
|
||||
.document_metadata()
|
||||
@@ -217,6 +217,7 @@ impl LayerSnapper {
|
||||
get_bbox_points(quad, &mut self.points_to_snap, values, document);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn snap_anchors(&mut self, snap_data: &mut SnapData, point: &SnapCandidatePoint, snap_results: &mut SnapResults, c: SnapConstraint, constrained_point: DVec2) {
|
||||
let mut best = None;
|
||||
for candidate in &self.points_to_snap {
|
||||
@@ -251,6 +252,7 @@ impl LayerSnapper {
|
||||
snap_results.points.push(result);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -275,7 +277,7 @@ fn normals_and_tangents(path: &SnapCandidatePath, normals: bool, tangents: bool,
|
||||
}
|
||||
snap_results.points.push(SnappedPoint {
|
||||
snapped_point_document: normal_point,
|
||||
target: SnapTarget::Geometry(GeometrySnapTarget::Normal),
|
||||
target: SnapTarget::Path(PathSnapTarget::NormalToPath),
|
||||
distance,
|
||||
tolerance,
|
||||
curves: [Some(path.document_curve), None],
|
||||
@@ -296,7 +298,7 @@ fn normals_and_tangents(path: &SnapCandidatePath, normals: bool, tangents: bool,
|
||||
}
|
||||
snap_results.points.push(SnappedPoint {
|
||||
snapped_point_document: tangent_point,
|
||||
target: SnapTarget::Geometry(GeometrySnapTarget::Tangent),
|
||||
target: SnapTarget::Path(PathSnapTarget::TangentToPath),
|
||||
distance,
|
||||
tolerance,
|
||||
curves: [Some(path.document_curve), None],
|
||||
@@ -317,6 +319,7 @@ struct SnapCandidatePath {
|
||||
target: SnapTarget,
|
||||
bounds: Option<Quad>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct SnapCandidatePoint {
|
||||
pub document_point: DVec2,
|
||||
@@ -330,6 +333,7 @@ impl SnapCandidatePoint {
|
||||
pub fn new(document_point: DVec2, source: SnapSource, target: SnapTarget) -> Self {
|
||||
Self::new_quad(document_point, source, target, None, true)
|
||||
}
|
||||
|
||||
pub fn new_quad(document_point: DVec2, source: SnapSource, target: SnapTarget, quad: Option<Quad>, alignment: bool) -> Self {
|
||||
Self {
|
||||
document_point,
|
||||
@@ -340,18 +344,22 @@ impl SnapCandidatePoint {
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_source(document_point: DVec2, source: SnapSource) -> Self {
|
||||
Self::new(document_point, source, SnapTarget::None)
|
||||
}
|
||||
|
||||
pub fn handle(document_point: DVec2) -> Self {
|
||||
Self::new_source(document_point, SnapSource::Geometry(GeometrySnapSource::AnchorWithFreeHandles))
|
||||
Self::new_source(document_point, SnapSource::Path(PathSnapSource::AnchorPointWithFreeHandles))
|
||||
}
|
||||
|
||||
pub fn handle_neighbors(document_point: DVec2, neighbors: impl Into<Vec<DVec2>>) -> Self {
|
||||
let mut point = Self::new_source(document_point, SnapSource::Geometry(GeometrySnapSource::AnchorWithFreeHandles));
|
||||
let mut point = Self::new_source(document_point, SnapSource::Path(PathSnapSource::AnchorPointWithFreeHandles));
|
||||
point.neighbors = neighbors.into();
|
||||
point
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct BBoxSnapValues {
|
||||
corner_source: SnapSource,
|
||||
@@ -363,41 +371,42 @@ pub struct BBoxSnapValues {
|
||||
}
|
||||
impl BBoxSnapValues {
|
||||
pub const BOUNDING_BOX: Self = Self {
|
||||
corner_source: SnapSource::BoundingBox(BoundingBoxSnapSource::Corner),
|
||||
corner_target: SnapTarget::BoundingBox(BoundingBoxSnapTarget::Corner),
|
||||
corner_source: SnapSource::BoundingBox(BoundingBoxSnapSource::CornerPoint),
|
||||
corner_target: SnapTarget::BoundingBox(BoundingBoxSnapTarget::CornerPoint),
|
||||
edge_source: SnapSource::BoundingBox(BoundingBoxSnapSource::EdgeMidpoint),
|
||||
edge_target: SnapTarget::BoundingBox(BoundingBoxSnapTarget::EdgeMidpoint),
|
||||
center_source: SnapSource::BoundingBox(BoundingBoxSnapSource::Center),
|
||||
center_target: SnapTarget::BoundingBox(BoundingBoxSnapTarget::Center),
|
||||
center_source: SnapSource::BoundingBox(BoundingBoxSnapSource::CenterPoint),
|
||||
center_target: SnapTarget::BoundingBox(BoundingBoxSnapTarget::CenterPoint),
|
||||
};
|
||||
|
||||
pub const ARTBOARD: Self = Self {
|
||||
corner_source: SnapSource::Artboard(ArtboardSnapSource::Corner),
|
||||
corner_target: SnapTarget::Artboard(ArtboardSnapTarget::Corner),
|
||||
corner_source: SnapSource::Artboard(ArtboardSnapSource::CornerPoint),
|
||||
corner_target: SnapTarget::Artboard(ArtboardSnapTarget::CornerPoint),
|
||||
edge_source: SnapSource::None,
|
||||
edge_target: SnapTarget::None,
|
||||
center_source: SnapSource::Artboard(ArtboardSnapSource::Center),
|
||||
center_target: SnapTarget::Artboard(ArtboardSnapTarget::Center),
|
||||
center_source: SnapSource::Artboard(ArtboardSnapSource::CenterPoint),
|
||||
center_target: SnapTarget::Artboard(ArtboardSnapTarget::CenterPoint),
|
||||
};
|
||||
|
||||
pub const ALIGN_BOUNDING_BOX: Self = Self {
|
||||
corner_source: SnapSource::Alignment(AlignmentSnapSource::BoundsCorner),
|
||||
corner_target: SnapTarget::Alignment(AlignmentSnapTarget::BoundsCorner),
|
||||
corner_source: SnapSource::Alignment(AlignmentSnapSource::BoundingBoxCornerPoint),
|
||||
corner_target: SnapTarget::Alignment(AlignmentSnapTarget::BoundingBoxCornerPoint),
|
||||
edge_source: SnapSource::None,
|
||||
edge_target: SnapTarget::None,
|
||||
center_source: SnapSource::Alignment(AlignmentSnapSource::BoundsCenter),
|
||||
center_target: SnapTarget::Alignment(AlignmentSnapTarget::BoundsCenter),
|
||||
center_source: SnapSource::Alignment(AlignmentSnapSource::BoundingBoxCenterPoint),
|
||||
center_target: SnapTarget::Alignment(AlignmentSnapTarget::BoundingBoxCenterPoint),
|
||||
};
|
||||
|
||||
pub const ALIGN_ARTBOARD: Self = Self {
|
||||
corner_source: SnapSource::Alignment(AlignmentSnapSource::ArtboardCorner),
|
||||
corner_target: SnapTarget::Alignment(AlignmentSnapTarget::ArtboardCorner),
|
||||
corner_source: SnapSource::Alignment(AlignmentSnapSource::ArtboardCornerPoint),
|
||||
corner_target: SnapTarget::Alignment(AlignmentSnapTarget::ArtboardCornerPoint),
|
||||
edge_source: SnapSource::None,
|
||||
edge_target: SnapTarget::None,
|
||||
center_source: SnapSource::Alignment(AlignmentSnapSource::ArtboardCenter),
|
||||
center_target: SnapTarget::Alignment(AlignmentSnapTarget::ArtboardCenter),
|
||||
center_source: SnapSource::Alignment(AlignmentSnapSource::ArtboardCenterPoint),
|
||||
center_target: SnapTarget::Alignment(AlignmentSnapTarget::ArtboardCenterPoint),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn get_bbox_points(quad: Quad, points: &mut Vec<SnapCandidatePoint>, values: BBoxSnapValues, document: &DocumentMessageHandler) {
|
||||
for index in 0..4 {
|
||||
let start = quad.0[index];
|
||||
@@ -409,6 +418,7 @@ pub fn get_bbox_points(quad: Quad, points: &mut Vec<SnapCandidatePoint>, values:
|
||||
points.push(SnapCandidatePoint::new_quad((start + end) / 2., values.edge_source, values.edge_target, Some(quad), false));
|
||||
}
|
||||
}
|
||||
|
||||
if document.snapping_state.target_enabled(values.center_target) {
|
||||
points.push(SnapCandidatePoint::new_quad(quad.center(), values.center_source, values.center_target, Some(quad), false));
|
||||
}
|
||||
@@ -417,10 +427,12 @@ pub fn get_bbox_points(quad: Quad, points: &mut Vec<SnapCandidatePoint>, values:
|
||||
fn handle_not_under(to_document: DAffine2) -> impl Fn(&DVec2) -> bool {
|
||||
move |&offset: &DVec2| to_document.transform_vector2(offset).length_squared() >= HIDE_HANDLE_DISTANCE * HIDE_HANDLE_DISTANCE
|
||||
}
|
||||
|
||||
fn subpath_anchor_snap_points(layer: LayerNodeIdentifier, subpath: &Subpath<PointId>, snap_data: &SnapData, points: &mut Vec<SnapCandidatePoint>, to_document: DAffine2) {
|
||||
let document = snap_data.document;
|
||||
|
||||
// Midpoints of linear segments
|
||||
if document.snapping_state.target_enabled(SnapTarget::Geometry(GeometrySnapTarget::LineMidpoint)) {
|
||||
if document.snapping_state.target_enabled(SnapTarget::Path(PathSnapTarget::LineMidpoint)) {
|
||||
for (index, curve) in subpath.iter().enumerate() {
|
||||
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;
|
||||
@@ -434,12 +446,13 @@ fn subpath_anchor_snap_points(layer: LayerNodeIdentifier, subpath: &Subpath<Poin
|
||||
if in_handle.is_none() && out_handle.is_none() {
|
||||
points.push(SnapCandidatePoint::new(
|
||||
to_document.transform_point2(curve.start() * 0.5 + curve.end * 0.5),
|
||||
SnapSource::Geometry(GeometrySnapSource::LineMidpoint),
|
||||
SnapTarget::Geometry(GeometrySnapTarget::LineMidpoint),
|
||||
SnapSource::Path(PathSnapSource::LineMidpoint),
|
||||
SnapTarget::Path(PathSnapTarget::LineMidpoint),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Anchors
|
||||
for (index, group) in subpath.manipulator_groups().iter().enumerate() {
|
||||
if snap_data.ignore_manipulator(layer, group.id) {
|
||||
@@ -452,19 +465,20 @@ fn subpath_anchor_snap_points(layer: LayerNodeIdentifier, subpath: &Subpath<Poin
|
||||
|
||||
let colinear = are_manipulator_handles_colinear(group, to_document, subpath, index);
|
||||
|
||||
if colinear && document.snapping_state.target_enabled(SnapTarget::Geometry(GeometrySnapTarget::AnchorWithColinearHandles)) {
|
||||
// Colinear handles
|
||||
// Colinear handles
|
||||
if colinear && document.snapping_state.target_enabled(SnapTarget::Path(PathSnapTarget::AnchorPointWithColinearHandles)) {
|
||||
points.push(SnapCandidatePoint::new(
|
||||
to_document.transform_point2(group.anchor),
|
||||
SnapSource::Geometry(GeometrySnapSource::AnchorWithColinearHandles),
|
||||
SnapTarget::Geometry(GeometrySnapTarget::AnchorWithColinearHandles),
|
||||
SnapSource::Path(PathSnapSource::AnchorPointWithColinearHandles),
|
||||
SnapTarget::Path(PathSnapTarget::AnchorPointWithColinearHandles),
|
||||
));
|
||||
} else if !colinear && document.snapping_state.target_enabled(SnapTarget::Geometry(GeometrySnapTarget::AnchorWithFreeHandles)) {
|
||||
// Free handles
|
||||
}
|
||||
// Free handles
|
||||
else if !colinear && document.snapping_state.target_enabled(SnapTarget::Path(PathSnapTarget::AnchorPointWithFreeHandles)) {
|
||||
points.push(SnapCandidatePoint::new(
|
||||
to_document.transform_point2(group.anchor),
|
||||
SnapSource::Geometry(GeometrySnapSource::AnchorWithFreeHandles),
|
||||
SnapTarget::Geometry(GeometrySnapTarget::AnchorWithFreeHandles),
|
||||
SnapSource::Path(PathSnapSource::AnchorPointWithFreeHandles),
|
||||
SnapTarget::Path(PathSnapTarget::AnchorPointWithFreeHandles),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ impl SnappedPoint {
|
||||
Self {
|
||||
snapped_point_document: point.document_point + translation,
|
||||
source: point.source,
|
||||
target: SnapTarget::Distribution(target),
|
||||
target: SnapTarget::DistributeEvenly(target),
|
||||
distribution_boxes_x,
|
||||
distribution_equal_distance_x: is_x.then_some(distances.equal),
|
||||
distribution_boxes_y,
|
||||
|
||||
Reference in New Issue
Block a user