Skip to content

Commit bc7a01d

Browse files
committed
fix self intersection metod
1 parent 883acf5 commit bc7a01d

6 files changed

Lines changed: 70 additions & 64 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Changelog
22

3+
## [0.6.9] - 2025-11-01
4+
- Fix arcline_has_self_intersection arcline_self_intersections
5+
36
## [0.6.8] - 2025-10-31
47
- Fix arcline_has_self_intersection arcline_self_intersections
58

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,4 +123,8 @@ assert_eq!(hull.len(), 4); // 4 points on convex hull
123123
let quarter_arc = arc(point(1.0, 0.0), point(0.0, 1.0), point(0.0, 0.0), 1.0);
124124
let bounding = arc_bounding_circle(&quarter_arc);
125125
assert_eq!(bounding.r, 0.7071067811865476); // sqrt(2)/2
126-
```
126+
```
127+
128+
## Related Projects
129+
130+
Togo is part of the open-sourced [Nest2D](https://nest2d.com) projects collection.

benches/bench_self_intersection.rs

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,29 @@ fn main() {
1414
let _ = arcline_has_self_intersection(&poly);
1515

1616
// Actual benchmark
17-
let start = Instant::now();
18-
let mut has_intersection = true;
17+
let start1000 = Instant::now();
18+
let mut has_intersection1000 = true;
1919
for _ in 0..1000 {
20-
has_intersection = arcline_has_self_intersection(&poly);
20+
has_intersection1000 = arcline_has_self_intersection(&poly);
2121
}
22-
let elapsed = start.elapsed();
22+
let elapsed1000 = start1000.elapsed();
23+
24+
let start200 = Instant::now();
25+
let mut has_intersection200 = true;
26+
for _ in 0..10000 {
27+
has_intersection200 = arcline_has_self_intersection(&poly);
28+
}
29+
let elapsed200 = start200.elapsed();
2330

2431
// ASSERT: poly1000 should have no self-intersections
25-
assert!(!has_intersection, "poly1000 must have no self-intersections");
32+
assert!(!has_intersection1000 && !has_intersection200, "poly1000 must have no self-intersections");
2633

27-
println!("\nResult: {} (no self-intersections verified)", if has_intersection { "INTERSECTS" } else { "CLEAN" });
28-
println!("Time: {:.4} ms ({:.0} µs)\n",
29-
elapsed.as_secs_f64() * 1000.0 / 1000.0,
30-
elapsed.as_secs_f64() * 1_000_000.0 / 1000.0);
34+
println!("Time for arcline1000 (1_000 it): {:.4} ms ({:.0} µs)",
35+
elapsed1000.as_secs_f64() * 1000.0 / 1000.0,
36+
elapsed1000.as_secs_f64() * 1_000_000.0 / 1000.0);
37+
println!("Time for arcline200 (10_000 it): {:.4} ms ({:.0} µs)\n",
38+
elapsed200.as_secs_f64() * 1000.0 / 1000.0,
39+
elapsed200.as_secs_f64() * 1_000_000.0 / 10000.0);
3140
println!("========================================");
3241
}
3342

src/algo/self_intersect.rs

Lines changed: 42 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -144,16 +144,13 @@ pub fn arcline_self_intersections(arcline: &Arcline) -> Vec<(usize, usize)> {
144144
return intersections;
145145
}
146146

147-
// Special case for two-element arclines: check both (0,1) and (1,0)
147+
// Special case for two-element arclines: check (0,1) only (undirected)
148148
if n == 2 {
149149
let arc0 = &arcline[0];
150150
let arc1 = &arcline[1];
151151
if is_really_intersecting(arc0, arc1) {
152152
intersections.push((0, 1));
153153
}
154-
if is_really_intersecting(arc1, arc0) {
155-
intersections.push((1, 0));
156-
}
157154
return intersections;
158155
}
159156

@@ -184,11 +181,12 @@ pub fn arcline_self_intersections(arcline: &Arcline) -> Vec<(usize, usize)> {
184181
}
185182

186183
// Check if last arc intersects with first arc (for closed arclines)
184+
// Only add if (n-1, 0) ordering (since we want i < j for undirected pairs)
187185
if n >= 2 {
188186
let last_arc = &arcline[n - 1];
189187
let first_arc = &arcline[0];
190188
if is_really_intersecting(last_arc, first_arc) {
191-
intersections.push((n - 1, 0));
189+
intersections.push((0, n - 1));
192190
}
193191
}
194192

@@ -266,13 +264,10 @@ mod tests {
266264
let seg1 = arcseg(point(0.0, 0.0), point(1.0, 1.0));
267265
let seg2 = arcseg(point(0.0, 1.0), point(1.0, 0.0));
268266
let arcline = vec![seg1, seg2];
269-
assert!(arcline_has_self_intersection(&arcline));
270-
let mut ints = arcline_self_intersections(&arcline);
271-
ints.sort();
272-
let mut expected = vec![(0, 1), (1, 0)];
273-
expected.sort();
274-
// Accept either (0,1) or (1,0) or both, since intersection is symmetric
275-
assert!(ints == vec![(0, 1)] || ints == vec![(1, 0)] || ints == expected);
267+
assert!(arcline_has_self_intersection(&arcline));
268+
let ints = arcline_self_intersections(&arcline);
269+
// Should have exactly one undirected pair (0, 1)
270+
assert_eq!(ints, vec![(0, 1)]);
276271
}
277272

278273
#[test]
@@ -291,12 +286,10 @@ mod tests {
291286
let arc1 = arc(point(0.0, 0.0), point(1.0, 0.0), point(0.5, 0.5), 1.0);
292287
let seg = arcseg(point(0.5, 0.5), point(0.5, -1.0));
293288
let arcline = vec![arc1, seg];
294-
assert!(arcline_has_self_intersection(&arcline));
295-
let mut ints = arcline_self_intersections(&arcline);
296-
ints.sort();
297-
let mut expected = vec![(0, 1), (1, 0)];
298-
expected.sort();
299-
assert!(ints == vec![(0, 1)] || ints == vec![(1, 0)] || ints == expected);
289+
assert!(arcline_has_self_intersection(&arcline));
290+
let ints = arcline_self_intersections(&arcline);
291+
// Should have exactly one undirected pair (0, 1)
292+
assert_eq!(ints, vec![(0, 1)]);
300293
}
301294

302295
#[test]
@@ -305,12 +298,10 @@ mod tests {
305298
let seg = arcseg(point(0.5, 0.5), point(0.5, -1.0));
306299
let arc1 = arc(point(0.0, 0.0), point(1.0, 0.0), point(0.5, 0.5), 1.0);
307300
let arcline = vec![seg, arc1];
308-
assert!(arcline_has_self_intersection(&arcline));
309-
let mut ints = arcline_self_intersections(&arcline);
310-
ints.sort();
311-
let mut expected = vec![(0, 1), (1, 0)];
312-
expected.sort();
313-
assert!(ints == vec![(0, 1)] || ints == vec![(1, 0)] || ints == expected);
301+
assert!(arcline_has_self_intersection(&arcline));
302+
let ints = arcline_self_intersections(&arcline);
303+
// Should have exactly one undirected pair (0, 1)
304+
assert_eq!(ints, vec![(0, 1)]);
314305
}
315306
use super::*;
316307

@@ -389,4 +380,31 @@ mod tests {
389380
// This depends on the exact geometry, may or may not intersect
390381
let _ = intersections; // Just verify the function works
391382
}
383+
384+
#[test]
385+
fn test_arcseg_arc_asymmetry() {
386+
let seg = arcseg(point(0.5, 0.5), point(0.5, -1.0));
387+
let arc1 = arc(point(0.0, 0.0), point(1.0, 0.0), point(0.5, 0.5), 1.0);
388+
let ab = is_really_intersecting(&arc1, &seg);
389+
let ba = is_really_intersecting(&seg, &arc1);
390+
assert_eq!(ab, ba, "is_really_intersecting not symmetric for arc/seg");
391+
}
392+
393+
#[test]
394+
fn test_arc_arc_asymmetry() {
395+
let arc1 = arc(point(-1.0, 0.0), point(1.0, 0.0), point(0.0, 1.0), 1.0);
396+
let arc2 = arc(point(0.0, -1.0), point(0.0, 1.0), point(1.0, 0.0), 1.0);
397+
let ab = is_really_intersecting(&arc1, &arc2);
398+
let ba = is_really_intersecting(&arc2, &arc1);
399+
assert_eq!(ab, ba, "is_really_intersecting not symmetric for arc/arc");
400+
}
401+
402+
#[test]
403+
fn test_arcseg_arcseg_asymmetry() {
404+
let seg1 = arcseg(point(0.0, 0.0), point(1.0, 1.0));
405+
let seg2 = arcseg(point(0.0, 1.0), point(1.0, 0.0));
406+
let ab = is_really_intersecting(&seg1, &seg2);
407+
let ba = is_really_intersecting(&seg2, &seg1);
408+
assert_eq!(ab, ba, "is_really_intersecting not symmetric for seg/seg");
409+
}
392410
}

tests/arc_asymmetry_tests.rs

Lines changed: 0 additions & 28 deletions
This file was deleted.

0 commit comments

Comments
 (0)