Skip to content

Commit 326e00c

Browse files
committed
Use derivative tiebreaker
1 parent 5dedb08 commit 326e00c

File tree

2 files changed

+65
-1
lines changed

2 files changed

+65
-1
lines changed

node-graph/gcore/src/subpath/structs.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,24 @@ impl BezierHandles {
228228
_ => self,
229229
}
230230
}
231+
232+
/// Returns the tangent vector at the start of the curve (t=0)
233+
pub fn derivative_at_start(&self, start: DVec2, end: DVec2) -> DVec2 {
234+
match self {
235+
BezierHandles::Linear => end - start,
236+
BezierHandles::Quadratic { handle } => 2.0 * (handle - start),
237+
BezierHandles::Cubic { handle_start, .. } => 3.0 * (handle_start - start),
238+
}
239+
}
240+
241+
/// Returns the tangent vector at the end of the curve (t=1)
242+
pub fn derivative_at_end(&self, start: DVec2, end: DVec2) -> DVec2 {
243+
match self {
244+
BezierHandles::Linear => end - start,
245+
BezierHandles::Quadratic { handle } => 2.0 * (end - handle),
246+
BezierHandles::Cubic { handle_end, .. } => 3.0 * (end - handle_end),
247+
}
248+
}
231249
}
232250

233251
/// Representation of a bezier curve with 2D points.

node-graph/gcore/src/vector/vector_attributes.rs

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1057,7 +1057,53 @@ impl Vector {
10571057
let pos_b = self.point_domain.positions()[b.1];
10581058
let angle_a = (pos_a - point_pos).y.atan2((pos_a - point_pos).x);
10591059
let angle_b = (pos_b - point_pos).y.atan2((pos_b - point_pos).x);
1060-
angle_a.partial_cmp(&angle_b).unwrap_or(std::cmp::Ordering::Equal)
1060+
1061+
// Compare angles
1062+
match angle_a.partial_cmp(&angle_b) {
1063+
Some(std::cmp::Ordering::Equal) | None => {
1064+
// Angles are equal (within floating point precision), use derivative as tiebreaker
1065+
const EPSILON: f64 = 1e-10;
1066+
if (angle_a - angle_b).abs() < EPSILON {
1067+
// Get segment indices to access handles
1068+
let seg_idx_a = self.segment_domain.ids().iter().position(|&id| id == a.0);
1069+
let seg_idx_b = self.segment_domain.ids().iter().position(|&id| id == b.0);
1070+
1071+
if let (Some(idx_a), Some(idx_b)) = (seg_idx_a, seg_idx_b) {
1072+
let handles_a = self.segment_domain.handles()[idx_a];
1073+
let handles_b = self.segment_domain.handles()[idx_b];
1074+
1075+
// Get start and end positions for the segments
1076+
let start_a = self.point_domain.positions()[self.segment_domain.start_point()[idx_a]];
1077+
let end_a = self.point_domain.positions()[self.segment_domain.end_point()[idx_a]];
1078+
let start_b = self.point_domain.positions()[self.segment_domain.start_point()[idx_b]];
1079+
let end_b = self.point_domain.positions()[self.segment_domain.end_point()[idx_b]];
1080+
1081+
// Determine derivative based on edge direction
1082+
// If reversed (a.2 == true), we're coming into the point, so use derivative_at_end
1083+
// Otherwise, we're leaving the point, so use derivative_at_start
1084+
let deriv_a = if a.2 {
1085+
handles_a.derivative_at_end(start_a, end_a)
1086+
} else {
1087+
handles_a.derivative_at_start(start_a, end_a)
1088+
};
1089+
1090+
let deriv_b = if b.2 {
1091+
handles_b.derivative_at_end(start_b, end_b)
1092+
} else {
1093+
handles_b.derivative_at_start(start_b, end_b)
1094+
};
1095+
1096+
// Compare derivative angles
1097+
let deriv_angle_a = deriv_a.y.atan2(deriv_a.x);
1098+
let deriv_angle_b = deriv_b.y.atan2(deriv_b.x);
1099+
1100+
return deriv_angle_a.partial_cmp(&deriv_angle_b).unwrap_or(std::cmp::Ordering::Equal);
1101+
}
1102+
}
1103+
std::cmp::Ordering::Equal
1104+
}
1105+
Some(ordering) => ordering,
1106+
}
10611107
});
10621108
}
10631109

0 commit comments

Comments
 (0)