Repository: GraphiteEditor/Graphite (language: rust)

Issue:
Outline entire layer for snap to intersections/paths
When snapping to an intersection or path, currently just the relevant bézier curve(s) are displayed. @Keavon wishes for the entire layer to be displayed instead.


Keavon notes:

> Lighting up just the relevant subpath involved in the snapping would be best, but all the layer's paths would be fine too if that's much easier to implement.

Hypercube notes:
> The issue with subpaths is that we don't have any way to identify them due to the mesh vector data format.


Discussion / hints:

Fix patch:
diff --git a/editor/src/messages/tool/common_functionality/snapping.rs b/editor/src/messages/tool/common_functionality/snapping.rs
index 9e402d41b6..858cdd506e 100644
--- a/editor/src/messages/tool/common_functionality/snapping.rs
+++ b/editor/src/messages/tool/common_functionality/snapping.rs
@@ -11,7 +11,7 @@ use crate::messages::portfolio::document::utility_types::document_metadata::Laye
 use crate::messages::portfolio::document::utility_types::misc::{GridSnapTarget, PathSnapTarget, SnapTarget};
 use crate::messages::prelude::*;
 
-use bezier_rs::{Subpath, TValue};
+use bezier_rs::TValue;
 use graphene_core::renderer::Quad;
 use graphene_core::vector::PointId;
 use graphene_std::renderer::Rect;
@@ -151,7 +151,7 @@ fn get_closest_intersection(snap_to: DVec2, curves: &[SnappedCurve]) -> Option<S
 						distance,
 						target: SnapTarget::Path(PathSnapTarget::IntersectionPoint),
 						tolerance: close.point.tolerance,
-						curves: [Some(close.document_curve), Some(far.document_curve)],
+						outline_layers: [Some(close.layer), Some(far.layer)],
 						source: close.point.source,
 						at_intersection: true,
 						constrained: true,
@@ -443,9 +443,9 @@ impl SnapManager {
 	pub fn draw_overlays(&mut self, snap_data: SnapData, overlay_context: &mut OverlayContext) {
 		let to_viewport = snap_data.document.metadata().document_to_viewport;
 		if let Some(ind) = &self.indicator {
-			for curve in &ind.curves {
-				let Some(curve) = curve else { continue };
-				overlay_context.outline([Subpath::from_bezier(curve)].iter(), to_viewport);
+			for layer in &ind.outline_layers {
+				let &Some(layer) = layer else { continue };
+				overlay_context.outline(snap_data.document.metadata().layer_outline(layer), snap_data.document.metadata().transform_to_viewport(layer));
 			}
 			if let Some(quad) = ind.target_bounds {
 				overlay_context.quad(to_viewport * quad, None);
diff --git a/editor/src/messages/tool/common_functionality/snapping/grid_snapper.rs b/editor/src/messages/tool/common_functionality/snapping/grid_snapper.rs
index f1a874ade9..9acae9095e 100644
--- a/editor/src/messages/tool/common_functionality/snapping/grid_snapper.rs
+++ b/editor/src/messages/tool/common_functionality/snapping/grid_snapper.rs
@@ -2,7 +2,6 @@ use super::*;
 
 use crate::messages::portfolio::document::utility_types::misc::{GridSnapTarget, GridSnapping, GridType, SnapTarget};
 
-use bezier_rs::Bezier;
 use glam::DVec2;
 use graphene_core::renderer::Quad;
 
@@ -172,10 +171,6 @@ impl GridSnapper {
 					at_intersection: false,
 					constrained: true,
 					source_bounds: point.quad,
-					curves: [
-						Some(Bezier::from_linear_dvec2(projected - constraint_direction * tolerance, projected + constraint_direction * tolerance)),
-						None,
-					],
 					distance,
 					tolerance,
 					..Default::default()
diff --git a/editor/src/messages/tool/common_functionality/snapping/layer_snapper.rs b/editor/src/messages/tool/common_functionality/snapping/layer_snapper.rs
index 1d1d9efd20..f289d6aa55 100644
--- a/editor/src/messages/tool/common_functionality/snapping/layer_snapper.rs
+++ b/editor/src/messages/tool/common_functionality/snapping/layer_snapper.rs
@@ -117,7 +117,7 @@ impl LayerSnapper {
 						target: path.target,
 						distance,
 						tolerance,
-						curves: [path.bounds.is_none().then_some(path.document_curve), None],
+						outline_layers: [path.bounds.is_none().then_some(path.layer), None],
 						source: point.source,
 						target_bounds: path.bounds,
 						..Default::default()
@@ -156,7 +156,7 @@ impl LayerSnapper {
 							target: path.target,
 							distance,
 							tolerance,
-							curves: [path.bounds.is_none().then_some(path.document_curve), None],
+							outline_layers: [path.bounds.is_none().then_some(path.layer), None],
 							source: point.source,
 							target_bounds: path.bounds,
 							at_intersection: true,
@@ -241,6 +241,7 @@ impl LayerSnapper {
 					tolerance,
 					constrained: true,
 					target_bounds: candidate.quad,
+					outline_layers: [candidate.outline_layer, None],
 					..Default::default()
 				});
 			}
@@ -277,7 +278,7 @@ fn normals_and_tangents(path: &SnapCandidatePath, normals: bool, tangents: bool,
 					target: SnapTarget::Path(PathSnapTarget::NormalToPath),
 					distance,
 					tolerance,
-					curves: [Some(path.document_curve), None],
+					outline_layers: [Some(path.layer), None],
 					source: point.source,
 					constrained: true,
 					..Default::default()
@@ -298,7 +299,7 @@ fn normals_and_tangents(path: &SnapCandidatePath, normals: bool, tangents: bool,
 					target: SnapTarget::Path(PathSnapTarget::TangentToPath),
 					distance,
 					tolerance,
-					curves: [Some(path.document_curve), None],
+					outline_layers: [Some(path.layer), None],
 					source: point.source,
 					constrained: true,
 					..Default::default()
@@ -323,27 +324,30 @@ pub struct SnapCandidatePoint {
 	pub source: SnapSource,
 	pub target: SnapTarget,
 	pub quad: Option<Quad>,
+	/// This layer is outlined if the snap candidate is used.
+	pub outline_layer: Option<LayerNodeIdentifier>,
 	pub neighbors: Vec<DVec2>,
 	pub alignment: bool,
 }
 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(document_point: DVec2, source: SnapSource, target: SnapTarget, outline_layer: Option<LayerNodeIdentifier>) -> Self {
+		Self::new_quad(document_point, source, target, None, outline_layer, true)
 	}
 
-	pub fn new_quad(document_point: DVec2, source: SnapSource, target: SnapTarget, quad: Option<Quad>, alignment: bool) -> Self {
+	pub fn new_quad(document_point: DVec2, source: SnapSource, target: SnapTarget, quad: Option<Quad>, outline_layer: Option<LayerNodeIdentifier>, alignment: bool) -> Self {
 		Self {
 			document_point,
 			source,
 			target,
 			quad,
+			outline_layer,
 			alignment,
 			..Default::default()
 		}
 	}
 
 	pub fn new_source(document_point: DVec2, source: SnapSource) -> Self {
-		Self::new(document_point, source, SnapTarget::None)
+		Self::new(document_point, source, SnapTarget::None, None)
 	}
 
 	pub fn handle(document_point: DVec2) -> Self {
@@ -409,15 +413,15 @@ pub fn get_bbox_points(quad: Quad, points: &mut Vec<SnapCandidatePoint>, values:
 		let start = quad.0[index];
 		let end = quad.0[(index + 1) % 4];
 		if document.snapping_state.target_enabled(values.corner_target) {
-			points.push(SnapCandidatePoint::new_quad(start, values.corner_source, values.corner_target, Some(quad), false));
+			points.push(SnapCandidatePoint::new_quad(start, values.corner_source, values.corner_target, Some(quad), None, false));
 		}
 		if document.snapping_state.target_enabled(values.edge_target) {
-			points.push(SnapCandidatePoint::new_quad((start + end) / 2., values.edge_source, values.edge_target, Some(quad), false));
+			points.push(SnapCandidatePoint::new_quad((start + end) / 2., values.edge_source, values.edge_target, Some(quad), None, 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));
+		points.push(SnapCandidatePoint::new_quad(quad.center(), values.center_source, values.center_target, Some(quad), None, false));
 	}
 }
 
@@ -445,6 +449,7 @@ fn subpath_anchor_snap_points(layer: LayerNodeIdentifier, subpath: &Subpath<Poin
 					to_document.transform_point2(curve.start() * 0.5 + curve.end * 0.5),
 					SnapSource::Path(PathSnapSource::LineMidpoint),
 					SnapTarget::Path(PathSnapTarget::LineMidpoint),
+					Some(layer),
 				));
 			}
 		}
@@ -468,6 +473,7 @@ fn subpath_anchor_snap_points(layer: LayerNodeIdentifier, subpath: &Subpath<Poin
 				to_document.transform_point2(group.anchor),
 				SnapSource::Path(PathSnapSource::AnchorPointWithColinearHandles),
 				SnapTarget::Path(PathSnapTarget::AnchorPointWithColinearHandles),
+				Some(layer),
 			));
 		}
 		// Free handles
@@ -476,6 +482,7 @@ fn subpath_anchor_snap_points(layer: LayerNodeIdentifier, subpath: &Subpath<Poin
 				to_document.transform_point2(group.anchor),
 				SnapSource::Path(PathSnapSource::AnchorPointWithFreeHandles),
 				SnapTarget::Path(PathSnapTarget::AnchorPointWithFreeHandles),
+				Some(layer),
 			));
 		}
 	}
diff --git a/editor/src/messages/tool/common_functionality/snapping/snap_results.rs b/editor/src/messages/tool/common_functionality/snapping/snap_results.rs
index 3299cf87d3..34bbc039b7 100644
--- a/editor/src/messages/tool/common_functionality/snapping/snap_results.rs
+++ b/editor/src/messages/tool/common_functionality/snapping/snap_results.rs
@@ -27,7 +27,8 @@ pub struct SnappedPoint {
 	pub fully_constrained: bool,
 	pub target_bounds: Option<Quad>,
 	pub source_bounds: Option<Quad>,
-	pub curves: [Option<Bezier>; 2],
+	/// These layer(s) are outlined in the overlays when the snap is used.
+	pub outline_layers: [Option<LayerNodeIdentifier>; 2],
 	pub distance: f64,
 	pub tolerance: f64,
 	pub distribution_boxes_x: VecDeque<Rect>,


Test patch: