MCPcopy Create free account
hub / github.com/Incline-Developers/Incline / segment_segment

Function segment_segment

src/model/kernel.rs:127–191  ·  view source on GitHub ↗

Classify how segments `a -> b` and `c -> d` relate in the XY plane. Crossing vs not is decided by exact orientation signs; crossing vs touching, and near-miss touches, by metric distance ([`XY_TOL`]).

(a: DVec2, b: DVec2, c: DVec2, d: DVec2)

Source from the content-addressed store, hash-verified

125 let o1 = orient2d(c, d, a);
126 let o2 = orient2d(c, d, b);
127 let o3 = orient2d(a, b, c);
128 let o4 = orient2d(a, b, d);
129
130 // Exact proper-crossing test: each segment's endpoints straddle the
131 // other's supporting line (allowing exactly-on-line as straddling so a
132 // vertex sitting exactly on the other segment still yields a point).
133 let straddles = (o1 >= 0.0) != (o2 >= 0.0) || (o1 > 0.0) != (o2 > 0.0);
134 let straddled = (o3 >= 0.0) != (o4 >= 0.0) || (o3 > 0.0) != (o4 > 0.0);
135 if straddles && straddled {
136 let r = b - a;
137 let s = d - c;
138 let denom = r.perp_dot(s);
139 // A true crossing guarantees denom != 0; the division is
140 // well-conditioned because the straddle test already bounded the
141 // configuration away from parallel-and-disjoint.
142 let t = ((c - a).perp_dot(s) / denom).clamp(0.0, 1.0);
143 let u = ((c - a).perp_dot(r) / denom).clamp(0.0, 1.0);
144 let point = a + r * t;
145 let near_endpoint = point.distance(a) <= XY_TOL || point.distance(b) <= XY_TOL || point.distance(c) <= XY_TOL || point.distance(d) <= XY_TOL;
146 return if near_endpoint {
147 SegSeg::Touching { point, t, u }
148 } else {
149 SegSeg::Crossing { point, t, u }
150 };
151 }
152
153 // No exact crossing: the segments may still pass within XY_TOL of each
154 // other at an endpoint (a touch that drifted apart by float noise).
155 let candidates = [(a, c, d, 0.0, None), (b, c, d, 1.0, None), (c, a, b, 0.0, Some(())), (d, a, b, 1.0, Some(()))];
156 let mut best: Option<(f64, DVec2, f64, f64)> = None;
157 for (p, s0, s1, fixed_param, swapped) in candidates {
158 let (proj, param) = project_onto_segment(p, s0, s1);
159 let dist = p.distance(proj);
160 if dist <= XY_TOL && best.is_none_or(|(bd, ..)| dist < bd) {
161 let (t, u) = if swapped.is_some() { (param, fixed_param) } else { (fixed_param, param) };
162 best = Some((dist, proj, t, u));
163 }
164 }
165 if let Some((_, point, t, u)) = best {
166 return SegSeg::Touching { point, t, u };
167 }
168 SegSeg::Disjoint
169}
170
171/// Signed perpendicular distance in metres from `point` to the directed line
172/// `a -> b`. Positive on the left of the line; sign is exact (via
173/// [`orient2d`]), magnitude is approximate. Returns `0.0` for a degenerate
174/// (zero-length) edge.
175pub(crate) fn signed_distance_to_line(point: DVec2, a: DVec2, b: DVec2) -> f64 {
176 let len = a.distance(b);
177 if len == 0.0 {
178 return 0.0;
179 }
180 orient2d(a, b, point) / len
181}
182
183/// Closest point on segment `a -> b` to `p`, with its clamped parameter.
184pub(crate) fn project_onto_segment(p: DVec2, a: DVec2, b: DVec2) -> (DVec2, f64) {

Calls 3

collinear_overlapFunction · 0.85
orient2dFunction · 0.85
project_onto_segmentFunction · 0.85