Skip to main content

klayout_geom/
size.rs

1//! Sizing (offset / Minkowski sum with a disk-equivalent).
2//!
3//! Positive `delta` grows the region, negative shrinks. `Miter` joins
4//! produce sharp corners (matching KLayout's default `db.Region.size`).
5
6use crate::region::{polygon_from_int_contour, Region};
7use i_overlay::mesh::outline::offset::OutlineOffset;
8use i_overlay::mesh::style::{LineJoin, OutlineStyle};
9use klayout_core::Polygon;
10use std::f64::consts::PI;
11
12#[derive(Copy, Clone, Debug)]
13pub enum SizeJoin {
14    /// Sharp corners up to a miter ratio. Matches KLayout default.
15    Miter,
16    /// Rounded outer corners.
17    Round,
18    /// Beveled corners.
19    Bevel,
20}
21
22pub fn size(r: &Region, delta: i64, join: SizeJoin) -> Region {
23    if delta == 0 || r.is_empty() {
24        return r.clone();
25    }
26    let shapes: Vec<Vec<Vec<[f64; 2]>>> = r
27        .polygons
28        .iter()
29        .map(polygon_to_floats)
30        .collect();
31    let style = OutlineStyle::<f64> {
32        outer_offset: delta as f64,
33        inner_offset: delta as f64,
34        join: match join {
35            SizeJoin::Miter => LineJoin::Miter(0.5 * PI),
36            SizeJoin::Round => LineJoin::Round(0.05 * PI),
37            SizeJoin::Bevel => LineJoin::Bevel,
38        },
39    };
40    let result = shapes.outline(&style);
41
42    let mut polys: Vec<Polygon> = Vec::with_capacity(result.len());
43    for shape in result {
44        let mut iter = shape.into_iter();
45        let Some(hull_f) = iter.next() else { continue };
46        let hull_int: Vec<(i64, i64)> = hull_f
47            .iter()
48            .map(|p| (p[0].round() as i64, p[1].round() as i64))
49            .collect();
50        let Some(mut poly) = polygon_from_int_contour(&hull_int) else { continue };
51        for hole_f in iter {
52            let hole_int: Vec<(i64, i64)> = hole_f
53                .iter()
54                .map(|p| (p[0].round() as i64, p[1].round() as i64))
55                .collect();
56            if hole_int.len() >= 3 {
57                poly.add_hole(
58                    hole_int
59                        .into_iter()
60                        .map(|(x, y)| klayout_core::Point::new(x, y)),
61                );
62            }
63        }
64        polys.push(poly);
65    }
66
67    let mut out = Region::empty();
68    out.set_polygons(polys);
69    out
70}
71
72fn polygon_to_floats(p: &Polygon) -> Vec<Vec<[f64; 2]>> {
73    // i_overlay expects CCW hull / CW holes. Our canonical form is the
74    // opposite (CW hull / CCW holes), so reverse on the way in.
75    let mut out: Vec<Vec<[f64; 2]>> = Vec::with_capacity(1 + p.holes.len());
76    out.push(
77        p.hull
78            .iter()
79            .rev()
80            .map(|pt| [pt.x as f64, pt.y as f64])
81            .collect(),
82    );
83    for hole in &p.holes {
84        out.push(
85            hole.iter()
86                .rev()
87                .map(|pt| [pt.x as f64, pt.y as f64])
88                .collect(),
89        );
90    }
91    out
92}