1use crate::Region;
17use klayout_core::{Bbox, Point, Polygon};
18
19#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
22pub struct Edge {
23 pub a: Point,
24 pub b: Point,
25}
26
27impl Edge {
28 pub const fn new(a: Point, b: Point) -> Self {
29 Self { a, b }
30 }
31
32 pub fn dx(&self) -> i64 {
33 self.b.x - self.a.x
34 }
35
36 pub fn dy(&self) -> i64 {
37 self.b.y - self.a.y
38 }
39
40 pub fn length_squared(&self) -> i128 {
41 let dx = self.dx() as i128;
42 let dy = self.dy() as i128;
43 dx * dx + dy * dy
44 }
45
46 pub fn length(&self) -> f64 {
47 (self.length_squared() as f64).sqrt()
48 }
49
50 pub fn midpoint(&self) -> Point {
51 Point::new((self.a.x + self.b.x) / 2, (self.a.y + self.b.y) / 2)
52 }
53
54 pub fn bbox(&self) -> Bbox {
55 Bbox::new(
56 Point::new(self.a.x.min(self.b.x), self.a.y.min(self.b.y)),
57 Point::new(self.a.x.max(self.b.x), self.a.y.max(self.b.y)),
58 )
59 }
60
61 pub fn angle_degrees(&self) -> f64 {
65 let mut a = (self.dy() as f64).atan2(self.dx() as f64).to_degrees();
66 if a < 0.0 {
67 a += 180.0;
68 }
69 if a >= 180.0 {
70 a -= 180.0;
71 }
72 a
73 }
74
75 pub fn is_horizontal(&self) -> bool {
76 self.dy() == 0 && self.dx() != 0
77 }
78
79 pub fn is_vertical(&self) -> bool {
80 self.dx() == 0 && self.dy() != 0
81 }
82
83 pub fn is_axis_aligned(&self) -> bool {
84 self.is_horizontal() || self.is_vertical()
85 }
86}
87
88#[derive(Default, Clone, Debug)]
89pub struct Edges {
90 edges: Vec<Edge>,
91}
92
93impl Edges {
94 pub fn empty() -> Self {
95 Self::default()
96 }
97
98 pub fn from_edges<I: IntoIterator<Item = Edge>>(it: I) -> Self {
99 Self {
100 edges: it.into_iter().collect(),
101 }
102 }
103
104 pub fn from_polygon(p: &Polygon) -> Self {
107 let mut edges = Vec::new();
108 push_loop(&mut edges, &p.hull);
109 for hole in &p.holes {
110 push_loop(&mut edges, hole);
111 }
112 Self { edges }
113 }
114
115 pub fn from_region(r: &Region) -> Self {
117 let mut edges = Vec::new();
118 for p in r.polygons() {
119 push_loop(&mut edges, &p.hull);
120 for hole in &p.holes {
121 push_loop(&mut edges, hole);
122 }
123 }
124 Self { edges }
125 }
126
127 pub fn len(&self) -> usize {
128 self.edges.len()
129 }
130
131 pub fn is_empty(&self) -> bool {
132 self.edges.is_empty()
133 }
134
135 pub fn edges(&self) -> &[Edge] {
136 &self.edges
137 }
138
139 pub fn into_inner(self) -> Vec<Edge> {
140 self.edges
141 }
142
143 pub fn centers(&self, length: i64) -> Edges {
147 if length <= 0 {
148 return Edges::empty();
149 }
150 let mut out = Vec::with_capacity(self.edges.len());
151 for e in &self.edges {
152 let len2 = e.length_squared();
153 if len2 == 0 {
154 continue;
155 }
156 let len = (len2 as f64).sqrt();
157 if (len as i64) <= length {
158 out.push(*e);
159 continue;
160 }
161 let t = length as f64 / (2.0 * len);
162 let mid = e.midpoint();
163 let dx = e.dx() as f64;
164 let dy = e.dy() as f64;
165 let ax = mid.x as f64 - dx * t;
166 let ay = mid.y as f64 - dy * t;
167 let bx = mid.x as f64 + dx * t;
168 let by = mid.y as f64 + dy * t;
169 out.push(Edge::new(
170 Point::new(ax.round() as i64, ay.round() as i64),
171 Point::new(bx.round() as i64, by.round() as i64),
172 ));
173 }
174 Edges::from_edges(out)
175 }
176
177 pub fn extended(&self, before: i64, after: i64) -> Edges {
181 let mut out = Vec::with_capacity(self.edges.len());
182 for e in &self.edges {
183 let len2 = e.length_squared();
184 if len2 == 0 {
185 out.push(*e);
186 continue;
187 }
188 let len = (len2 as f64).sqrt();
189 let dx = e.dx() as f64 / len;
190 let dy = e.dy() as f64 / len;
191 let ax = (e.a.x as f64) - dx * (before as f64);
192 let ay = (e.a.y as f64) - dy * (before as f64);
193 let bx = (e.b.x as f64) + dx * (after as f64);
194 let by = (e.b.y as f64) + dy * (after as f64);
195 out.push(Edge::new(
196 Point::new(ax.round() as i64, ay.round() as i64),
197 Point::new(bx.round() as i64, by.round() as i64),
198 ));
199 }
200 Edges::from_edges(out)
201 }
202
203 pub fn with_angle(&self, angle_deg: f64, tolerance_deg: f64) -> Edges {
206 Edges::from_edges(self.edges.iter().copied().filter(|e| {
207 if e.length_squared() == 0 {
208 return false;
209 }
210 angles_close(e.angle_degrees(), angle_deg, tolerance_deg)
211 }))
212 }
213
214 pub fn not_with_angle(&self, angle_deg: f64, tolerance_deg: f64) -> Edges {
215 Edges::from_edges(self.edges.iter().copied().filter(|e| {
216 if e.length_squared() == 0 {
217 return false;
218 }
219 !angles_close(e.angle_degrees(), angle_deg, tolerance_deg)
220 }))
221 }
222
223 pub fn length_at_least(&self, min: i64) -> Edges {
224 let m2 = (min as i128) * (min as i128);
225 Edges::from_edges(
226 self.edges
227 .iter()
228 .copied()
229 .filter(|e| e.length_squared() >= m2),
230 )
231 }
232
233 pub fn length_at_most(&self, max: i64) -> Edges {
234 let m2 = (max as i128) * (max as i128);
235 Edges::from_edges(
236 self.edges
237 .iter()
238 .copied()
239 .filter(|e| e.length_squared() <= m2),
240 )
241 }
242
243 pub fn interacting(&self, other: &Edges) -> Edges {
247 if self.is_empty() || other.is_empty() {
248 return Edges::empty();
249 }
250 let other_bboxes: Vec<Bbox> = other.edges.iter().map(|e| e.bbox()).collect();
251 Edges::from_edges(self.edges.iter().copied().filter(|e| {
252 let eb = e.bbox();
253 other_bboxes.iter().any(|ob| eb.intersects(ob))
254 }))
255 }
256
257 pub fn outside_part(&self, region: &Region) -> Edges {
261 clip_edges(&self.edges, region, false)
262 }
263
264 pub fn inside_part(&self, region: &Region) -> Edges {
266 clip_edges(&self.edges, region, true)
267 }
268}
269
270fn push_loop(out: &mut Vec<Edge>, pts: &[Point]) {
271 let n = pts.len();
272 if n < 2 {
273 return;
274 }
275 for i in 0..n {
276 let a = pts[i];
277 let b = pts[(i + 1) % n];
278 if a != b {
279 out.push(Edge::new(a, b));
280 }
281 }
282}
283
284fn angles_close(a: f64, b: f64, tol: f64) -> bool {
285 let mut diff = (a - b).abs() % 180.0;
286 if diff > 90.0 {
287 diff = 180.0 - diff;
288 }
289 diff <= tol
290}
291
292fn clip_edges(edges: &[Edge], region: &Region, inside: bool) -> Edges {
301 if edges.is_empty() {
302 return Edges::empty();
303 }
304 if region.is_empty() {
305 return if inside {
306 Edges::empty()
307 } else {
308 Edges::from_edges(edges.iter().copied())
309 };
310 }
311 let mut out = Vec::with_capacity(edges.len());
312 for e in edges {
313 if !e.is_axis_aligned() {
314 out.push(*e);
315 continue;
316 }
317 let segments = clip_axis_aligned(e, region, inside);
318 out.extend(segments);
319 }
320 Edges::from_edges(out)
321}
322
323fn clip_axis_aligned(e: &Edge, region: &Region, inside: bool) -> Vec<Edge> {
324 let horizontal = e.is_horizontal();
325 let (axis_lo, axis_hi) = if horizontal {
326 let lo = e.a.x.min(e.b.x);
327 let hi = e.a.x.max(e.b.x);
328 (lo, hi)
329 } else {
330 let lo = e.a.y.min(e.b.y);
331 let hi = e.a.y.max(e.b.y);
332 (lo, hi)
333 };
334 let perp = if horizontal { e.a.y } else { e.a.x };
335 let bbox = e.bbox();
336
337 let mut splits: Vec<i64> = vec![axis_lo, axis_hi];
340 for poly in region.polygons() {
341 if !bbox.intersects(&poly.bbox()) {
342 continue;
343 }
344 for loop_pts in std::iter::once(poly.hull.as_slice()).chain(poly.holes.iter().map(|h| h.as_slice())) {
345 let n = loop_pts.len();
346 for i in 0..n {
347 let a = loop_pts[i];
348 let b = loop_pts[(i + 1) % n];
349 if let Some(x) = axis_aligned_crossing(a, b, perp, horizontal, axis_lo, axis_hi) {
350 splits.push(x);
351 }
352 }
353 }
354 }
355 splits.sort();
356 splits.dedup();
357
358 let mut segments = Vec::new();
359 for w in splits.windows(2) {
360 let (s, t) = (w[0], w[1]);
361 if s == t {
362 continue;
363 }
364 let mid_axis = (s + t) / 2;
365 let mid_point = if horizontal {
366 Point::new(mid_axis, perp)
367 } else {
368 Point::new(perp, mid_axis)
369 };
370 let in_region = region_contains_point(region, mid_point);
371 if inside == in_region {
372 let p_a = if horizontal {
373 Point::new(s, perp)
374 } else {
375 Point::new(perp, s)
376 };
377 let p_b = if horizontal {
378 Point::new(t, perp)
379 } else {
380 Point::new(perp, t)
381 };
382 let forward_axis = if horizontal { e.b.x - e.a.x } else { e.b.y - e.a.y } >= 0;
384 if forward_axis {
385 segments.push(Edge::new(p_a, p_b));
386 } else {
387 segments.push(Edge::new(p_b, p_a));
388 }
389 }
390 }
391 segments
392}
393
394fn axis_aligned_crossing(
398 a: Point,
399 b: Point,
400 perp: i64,
401 horizontal: bool,
402 lo: i64,
403 hi: i64,
404) -> Option<i64> {
405 let (perp_a, perp_b, axis_a, axis_b) = if horizontal {
406 (a.y, b.y, a.x, b.x)
407 } else {
408 (a.x, b.x, a.y, b.y)
409 };
410 if perp_a == perp_b {
411 if perp_a == perp {
413 let lo_seg = axis_a.min(axis_b).max(lo);
414 let hi_seg = axis_a.max(axis_b).min(hi);
415 if lo_seg < hi_seg {
416 return Some(lo_seg);
420 }
421 }
422 return None;
423 }
424 if (perp_a < perp && perp_b < perp) || (perp_a > perp && perp_b > perp) {
425 return None;
426 }
427 let t = (perp - perp_a) as f64 / (perp_b - perp_a) as f64;
428 let axis = (axis_a as f64 + t * (axis_b as f64 - axis_a as f64)).round() as i64;
429 if axis < lo || axis > hi {
430 return None;
431 }
432 Some(axis)
433}
434
435fn region_contains_point(r: &Region, p: Point) -> bool {
436 for poly in r.polygons() {
437 if !poly.bbox().contains(p) {
438 continue;
439 }
440 if point_in_polygon(p, &poly.hull) {
441 let in_hole = poly.holes.iter().any(|h| point_in_polygon(p, h));
442 if !in_hole {
443 return true;
444 }
445 }
446 }
447 false
448}
449
450fn point_in_polygon(p: Point, ring: &[Point]) -> bool {
451 let mut inside = false;
454 let n = ring.len();
455 if n < 3 {
456 return false;
457 }
458 for i in 0..n {
459 let a = ring[i];
460 let b = ring[(i + 1) % n];
461 let crosses = (a.y > p.y) != (b.y > p.y);
462 if crosses {
463 let t = (p.y - a.y) as f64 / (b.y - a.y) as f64;
464 let x_at = a.x as f64 + t * (b.x - a.x) as f64;
465 if x_at > p.x as f64 {
466 inside = !inside;
467 }
468 }
469 }
470 inside
471}
472
473#[cfg(test)]
474mod tests {
475 use super::*;
476 use klayout_core::Polygon;
477
478 fn pt(x: i64, y: i64) -> Point {
479 Point::new(x, y)
480 }
481
482 #[test]
483 fn polygon_edges_are_directed() {
484 let p = Polygon::rect(Bbox::new(pt(0, 0), pt(10, 5)));
485 let e = Edges::from_polygon(&p);
486 assert_eq!(e.len(), 4);
487 let dirs: Vec<(i64, i64)> = e.edges().iter().map(|x| (x.dx(), x.dy())).collect();
490 assert!(dirs.contains(&(10, 0)));
491 assert!(dirs.contains(&(0, 5)));
492 assert!(dirs.contains(&(-10, 0)));
493 assert!(dirs.contains(&(0, -5)));
494 }
495
496 #[test]
497 fn with_angle_filters_horizontals() {
498 let p = Polygon::rect(Bbox::new(pt(0, 0), pt(10, 5)));
499 let e = Edges::from_polygon(&p);
500 let h = e.with_angle(0.0, 1.0);
501 assert_eq!(h.len(), 2); let v = e.with_angle(90.0, 1.0);
503 assert_eq!(v.len(), 2); }
505
506 #[test]
507 fn length_filter() {
508 let p = Polygon::rect(Bbox::new(pt(0, 0), pt(10, 5)));
509 let e = Edges::from_polygon(&p);
510 assert_eq!(e.length_at_least(8).len(), 2); assert_eq!(e.length_at_most(6).len(), 2); }
513
514 #[test]
515 fn centers_emits_centered_segment() {
516 let e = Edges::from_edges([Edge::new(pt(0, 0), pt(10, 0))]);
517 let c = e.centers(2);
518 assert_eq!(c.len(), 1);
519 let seg = c.edges()[0];
520 assert_eq!(seg.a, pt(4, 0));
521 assert_eq!(seg.b, pt(6, 0));
522 }
523
524 #[test]
525 fn extended_grows_endpoints() {
526 let e = Edges::from_edges([Edge::new(pt(0, 0), pt(10, 0))]);
527 let x = e.extended(2, 3);
528 assert_eq!(x.edges()[0].a, pt(-2, 0));
529 assert_eq!(x.edges()[0].b, pt(13, 0));
530 }
531
532 #[test]
533 fn interacting_finds_overlapping_bboxes() {
534 let a = Edges::from_edges([Edge::new(pt(0, 0), pt(10, 0))]);
535 let b = Edges::from_edges([
536 Edge::new(pt(5, 0), pt(5, 10)),
537 Edge::new(pt(100, 100), pt(110, 100)),
538 ]);
539 let i = a.interacting(&b);
540 assert_eq!(i.len(), 1);
541 }
542
543 #[test]
544 fn outside_part_clips_against_region() {
545 let e = Edges::from_edges([Edge::new(pt(0, 0), pt(20, 0))]);
547 let r = Region::from_polygons([Polygon::rect(Bbox::new(pt(5, -5), pt(15, 5)))]);
548 let out = e.outside_part(&r);
549 assert_eq!(out.len(), 2);
551 }
552
553 #[test]
554 fn inside_part_clips_against_region() {
555 let e = Edges::from_edges([Edge::new(pt(0, 0), pt(20, 0))]);
556 let r = Region::from_polygons([Polygon::rect(Bbox::new(pt(5, -5), pt(15, 5)))]);
557 let inside = e.inside_part(&r);
558 assert_eq!(inside.len(), 1);
559 let seg = inside.edges()[0];
560 assert_eq!(seg.bbox().min.x, 5);
561 assert_eq!(seg.bbox().max.x, 15);
562 }
563}