-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmerge.py
More file actions
62 lines (46 loc) · 2.43 KB
/
Copy pathmerge.py
File metadata and controls
62 lines (46 loc) · 2.43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
"""Merge polygons within a layer.
Overlapping or adjacent polygons on the same layer are unioned into the fewest shapes that cover the same area.
This reduces polygon count (smaller files, cleaner booleans downstream) without changing what the layer covers.
By default every layer is merged; pass a set of layers to merge only those.
"""
from __future__ import annotations
import argparse
import numpy as np
import load as _load
def merge(layout, layers=None):
"""Return a new Layout with each (selected) layer's polygons unioned."""
import gdstk
want = None if layers is None else {tuple(l) for l in layers}
out = _load.Layout(name=layout.name, labels=list(layout.labels))
for layer, polys in layout.polys.items():
if (want is not None and layer not in want) or len(polys) <= 1:
out.polys[layer] = list(polys)
continue
unioned = gdstk.boolean([gdstk.Polygon(np.asarray(p, float)) for p in polys], [], "or")
out.polys[layer] = [np.asarray(m.points, float) for m in unioned]
return out
def _build():
import gdstk
lib = gdstk.Library("m")
c = lib.new_cell("c")
c.add(gdstk.rectangle((0, 0), (2, 2), layer=1)) # overlaps the next by a 1x1 corner
c.add(gdstk.rectangle((1, 1), (3, 3), layer=1))
c.add(gdstk.rectangle((10, 10), (12, 12), layer=2)) # lone rectangle on another layer
return lib
def _validate():
layout = _load.from_gdstk_library(_build())
merged = merge(layout)
ok_count = len(merged.polys[(1, 0)]) == 1 # two overlapping rects -> one shape
ok_area = abs(merged.area_on((1, 0)) - 7.0) < 1e-9 # union area 4 + 4 - 1, not 8
ok_other = len(merged.polys[(2, 0)]) == 1 and abs(merged.area_on((2, 0)) - 4.0) < 1e-9
ok_select = len(merge(layout, layers=[(2, 0)]).polys[(1, 0)]) == 2 # only (2,0) merged, (1,0) untouched
for label, cond in [("overlap unioned to one shape", ok_count), ("covered area preserved (7)", ok_area),
("other layer intact", ok_other), ("selective merge", ok_select)]:
print(f"[{label}] {'PASS' if cond else 'FAIL'}")
print("RESULT:", "PASS" if all([ok_count, ok_area, ok_other, ok_select]) else "FAIL")
def main():
ap = argparse.ArgumentParser(description="merge polygons within a layer (boolean union)")
ap.add_argument("--validate", action="store_true")
ap.parse_args(); _validate()
if __name__ == "__main__":
main()