Skip to content

Commit af4f57e

Browse files
moosama76Keavon
andauthored
Add "Perpendicular to Endpoint" snapping target (#2581)
* Perpendicular snap for line's endpoints * move comment * Code review --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
1 parent 435a6da commit af4f57e

File tree

2 files changed

+41
-1
lines changed

2 files changed

+41
-1
lines changed

editor/src/messages/portfolio/document/utility_types/misc.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ impl SnappingState {
9999
PathSnapTarget::NormalToPath => self.path.normal_to_path,
100100
PathSnapTarget::TangentToPath => self.path.tangent_to_path,
101101
PathSnapTarget::IntersectionPoint => self.path.path_intersection_point,
102+
PathSnapTarget::PerpendicularToEndpoint => self.path.perpendicular_from_endpoint,
102103
},
103104
SnapTarget::Artboard(_) => self.artboards,
104105
SnapTarget::Grid(_) => self.grid_snapping,
@@ -142,6 +143,7 @@ pub struct PathSnapping {
142143
pub tangent_to_path: bool,
143144
pub path_intersection_point: bool,
144145
pub align_with_anchor_point: bool, // TODO: Rename
146+
pub perpendicular_from_endpoint: bool,
145147
}
146148

147149
impl Default for PathSnapping {
@@ -154,6 +156,7 @@ impl Default for PathSnapping {
154156
tangent_to_path: true,
155157
path_intersection_point: true,
156158
align_with_anchor_point: true,
159+
perpendicular_from_endpoint: true,
157160
}
158161
}
159162
}
@@ -476,6 +479,7 @@ pub enum PathSnapTarget {
476479
NormalToPath,
477480
TangentToPath,
478481
IntersectionPoint,
482+
PerpendicularToEndpoint,
479483
}
480484

481485
impl fmt::Display for PathSnapTarget {
@@ -487,6 +491,7 @@ impl fmt::Display for PathSnapTarget {
487491
PathSnapTarget::NormalToPath => write!(f, "Path: Normal to Path"),
488492
PathSnapTarget::TangentToPath => write!(f, "Path: Tangent to Path"),
489493
PathSnapTarget::IntersectionPoint => write!(f, "Path: Intersection Point"),
494+
PathSnapTarget::PerpendicularToEndpoint => write!(f, "Path: Perp. to Endpoint"),
490495
}
491496
}
492497
}
@@ -533,6 +538,7 @@ pub enum AlignmentSnapTarget {
533538
ArtboardCenterPoint,
534539
AlignWithAnchorPoint,
535540
IntersectionPoint,
541+
PerpendicularToEndpoint,
536542
}
537543

538544
impl fmt::Display for AlignmentSnapTarget {
@@ -544,6 +550,7 @@ impl fmt::Display for AlignmentSnapTarget {
544550
AlignmentSnapTarget::ArtboardCenterPoint => write!(f, "{}", ArtboardSnapTarget::CenterPoint),
545551
AlignmentSnapTarget::AlignWithAnchorPoint => write!(f, "{}", PathSnapTarget::AnchorPointWithColinearHandles),
546552
AlignmentSnapTarget::IntersectionPoint => write!(f, "{}", PathSnapTarget::IntersectionPoint),
553+
AlignmentSnapTarget::PerpendicularToEndpoint => write!(f, "{}", PathSnapTarget::PerpendicularToEndpoint),
547554
}
548555
}
549556
}

editor/src/messages/tool/common_functionality/snapping/alignment_snapper.rs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,46 @@ impl AlignmentSnapper {
5959
// TODO: snap handle points
6060
let document = snap_data.document;
6161
let tolerance = snap_tolerance(document);
62-
62+
let tolerance_squared = tolerance.powi(2);
6363
let mut snap_x: Option<SnappedPoint> = None;
6464
let mut snap_y: Option<SnappedPoint> = None;
6565

6666
for target_point in self.bounding_box_points.iter().chain(unselected_geometry) {
6767
let target_position = target_point.document_point;
6868

69+
// Perpendicular snap for line's endpoints
70+
if let Some(quad) = target_point.quad.map(|q| q.0) {
71+
if quad[0] == quad[3] && quad[1] == quad[2] && quad[0] == target_point.document_point {
72+
let [p1, p2, ..] = quad;
73+
let direction = (p2 - p1).normalize();
74+
let normal = DVec2::new(-direction.y, direction.x);
75+
76+
for endpoint in [p1, p2] {
77+
if let Some(perpendicular_snap) = Quad::intersect_rays(point.document_point, direction, endpoint, normal) {
78+
let distance_squared = point.document_point.distance_squared(perpendicular_snap);
79+
if distance_squared < tolerance_squared {
80+
let distance = distance_squared.sqrt();
81+
let distance_to_align_target = perpendicular_snap.distance_squared(endpoint).sqrt();
82+
83+
let snap_point = SnappedPoint {
84+
snapped_point_document: perpendicular_snap,
85+
source: point.source,
86+
target: SnapTarget::Alignment(AlignmentSnapTarget::PerpendicularToEndpoint),
87+
target_bounds: Some(Quad(quad)),
88+
distance,
89+
tolerance,
90+
distance_to_align_target,
91+
fully_constrained: false,
92+
at_intersection: true,
93+
alignment_target_x: Some(endpoint),
94+
..Default::default()
95+
};
96+
snap_results.points.push(snap_point);
97+
}
98+
}
99+
}
100+
}
101+
}
69102
let [point_on_x, point_on_y] = if let SnapConstraint::Line { origin, direction } = constraint {
70103
[
71104
Quad::intersect_rays(target_point.document_point, DVec2::Y, origin, direction),

0 commit comments

Comments
 (0)