0
# Shape Collections and Boolean Operations
1
2
Advanced geometric operations including boolean operations (AND, OR, XOR, NOT), sizing, merging, and comprehensive region analysis for layout verification and processing.
3
4
## Capabilities
5
6
### Region Operations
7
8
The Region class provides powerful bulk operations on collections of polygons, essential for design rule checking and layout processing.
9
10
```python { .api }
11
class Region:
12
def __init__(self, shapes=None):
13
"""
14
Create a region from shapes or empty region.
15
16
Parameters:
17
- shapes: Initial shapes (Polygon, Box, or iterable of shapes)
18
"""
19
20
def insert(self, shape) -> None:
21
"""
22
Insert a shape into the region.
23
24
Parameters:
25
- shape: Polygon, Box, or other geometric shape
26
"""
27
28
def __and__(self, other: Region) -> Region:
29
"""Boolean AND operation (intersection)."""
30
31
def __or__(self, other: Region) -> Region:
32
"""Boolean OR operation (union)."""
33
34
def __xor__(self, other: Region) -> Region:
35
"""Boolean XOR operation (exclusive or)."""
36
37
def __sub__(self, other: Region) -> Region:
38
"""Boolean subtraction (difference)."""
39
40
def sized(self, dx: int, dy: int = None) -> Region:
41
"""
42
Size operation (grow/shrink polygons).
43
44
Parameters:
45
- dx: X sizing amount (positive to grow)
46
- dy: Y sizing amount (defaults to dx)
47
48
Returns:
49
Region: Sized region
50
"""
51
52
def merged(self) -> Region:
53
"""Merge overlapping and touching polygons."""
54
55
def smoothed(self, d: int) -> Region:
56
"""
57
Smooth region by removing small notches and gaps.
58
59
Parameters:
60
- d: Smoothing distance
61
"""
62
63
def rounded_corners(self, radius_inner: int, radius_outer: int, num_points: int) -> Region:
64
"""
65
Round corners of polygons.
66
67
Parameters:
68
- radius_inner: Inner corner radius
69
- radius_outer: Outer corner radius
70
- num_points: Points per quarter circle
71
"""
72
73
def area(self) -> int:
74
"""Calculate total area of all polygons."""
75
76
def perimeter(self) -> int:
77
"""Calculate total perimeter of all polygons."""
78
79
def bbox(self) -> Box:
80
"""Get bounding box of entire region."""
81
82
def is_empty(self) -> bool:
83
"""Check if region contains no shapes."""
84
85
def count(self) -> int:
86
"""Get number of polygons in region."""
87
88
def each(self):
89
"""Iterate over polygons in region."""
90
91
def select_interacting(self, other: Region) -> Region:
92
"""Select polygons that interact with other region."""
93
94
def select_not_interacting(self, other: Region) -> Region:
95
"""Select polygons that don't interact with other region."""
96
97
def select_overlapping(self, other: Region) -> Region:
98
"""Select polygons that overlap with other region."""
99
100
def select_not_overlapping(self, other: Region) -> Region:
101
"""Select polygons that don't overlap with other region."""
102
103
def width_check(self, d: int) -> EdgePairs:
104
"""
105
Check for width violations.
106
107
Parameters:
108
- d: Minimum width
109
110
Returns:
111
EdgePairs: Width violation edge pairs
112
"""
113
114
def space_check(self, d: int) -> EdgePairs:
115
"""
116
Check for spacing violations.
117
118
Parameters:
119
- d: Minimum spacing
120
121
Returns:
122
EdgePairs: Spacing violation edge pairs
123
"""
124
```
125
126
### Edge Collections
127
128
```python { .api }
129
class Edges:
130
def __init__(self, shapes=None):
131
"""
132
Create edge collection from shapes.
133
134
Parameters:
135
- shapes: Initial shapes or edges
136
"""
137
138
def insert(self, edge: Edge) -> None:
139
"""Insert an edge into the collection."""
140
141
def merged(self) -> Edges:
142
"""Merge connected and overlapping edges."""
143
144
def extended(self, ext_begin: int, ext_end: int) -> Edges:
145
"""
146
Extend edges at both ends.
147
148
Parameters:
149
- ext_begin: Extension at beginning
150
- ext_end: Extension at end
151
"""
152
153
def length(self) -> int:
154
"""Get total length of all edges."""
155
156
def bbox(self) -> Box:
157
"""Get bounding box of all edges."""
158
159
def each(self):
160
"""Iterate over edges."""
161
162
def with_length(self, min_length: int, max_length: int = None) -> Edges:
163
"""
164
Select edges within length range.
165
166
Parameters:
167
- min_length: Minimum length
168
- max_length: Maximum length (unlimited if None)
169
"""
170
171
def with_angle(self, min_angle: float, max_angle: float) -> Edges:
172
"""Select edges within angle range (in degrees)."""
173
```
174
175
### Edge Pairs (for DRC Results)
176
177
```python { .api }
178
class EdgePairs:
179
def __init__(self):
180
"""Create empty edge pair collection."""
181
182
def insert(self, edge1: Edge, edge2: Edge) -> None:
183
"""
184
Insert an edge pair.
185
186
Parameters:
187
- edge1: First edge
188
- edge2: Second edge
189
"""
190
191
def count(self) -> int:
192
"""Get number of edge pairs."""
193
194
def each(self):
195
"""Iterate over edge pairs."""
196
197
def polygons(self, enlargement: int = 0) -> Region:
198
"""
199
Convert edge pairs to polygons for visualization.
200
201
Parameters:
202
- enlargement: Polygon enlargement around edges
203
204
Returns:
205
Region: Region representing the edge pairs
206
"""
207
208
def first_edges(self) -> Edges:
209
"""Get collection of first edges from all pairs."""
210
211
def second_edges(self) -> Edges:
212
"""Get collection of second edges from all pairs."""
213
```
214
215
### Text Collections
216
217
```python { .api }
218
class Texts:
219
def __init__(self, shapes=None):
220
"""Create text collection."""
221
222
def insert(self, text: Text) -> None:
223
"""Insert a text object."""
224
225
def each(self):
226
"""Iterate over text objects."""
227
228
def count(self) -> int:
229
"""Get number of text objects."""
230
231
def bbox(self) -> Box:
232
"""Get bounding box of all texts."""
233
234
def with_text(self, pattern: str) -> Texts:
235
"""
236
Select texts matching pattern.
237
238
Parameters:
239
- pattern: Text pattern to match
240
"""
241
```
242
243
### Deep Region Operations
244
245
```python { .api }
246
class DeepShapes:
247
"""Deep shapes for hierarchical operations."""
248
249
def region(self, layer: int) -> Region:
250
"""Get region for layer with hierarchy flattening."""
251
252
def edges(self, layer: int) -> Edges:
253
"""Get edges for layer with hierarchy flattening."""
254
```
255
256
## Usage Examples
257
258
### Basic Boolean Operations
259
260
```python
261
import klayout.db as db
262
263
# Create two regions
264
region1 = db.Region()
265
region1.insert(db.Box(0, 0, 100, 100))
266
region1.insert(db.Box(50, 50, 150, 150))
267
268
region2 = db.Region()
269
region2.insert(db.Box(25, 25, 75, 75))
270
region2.insert(db.Box(125, 25, 175, 75))
271
272
# Boolean operations
273
intersection = region1 & region2 # AND
274
union = region1 | region2 # OR
275
difference = region1 - region2 # SUBTRACT
276
symmetric_diff = region1 ^ region2 # XOR
277
278
print(f"Region1 area: {region1.area()}")
279
print(f"Region2 area: {region2.area()}")
280
print(f"Intersection area: {intersection.area()}")
281
print(f"Union area: {union.area()}")
282
```
283
284
### Sizing and Merging Operations
285
286
```python
287
import klayout.db as db
288
289
# Create region with overlapping shapes
290
region = db.Region()
291
region.insert(db.Box(0, 0, 50, 50))
292
region.insert(db.Box(40, 0, 90, 50))
293
region.insert(db.Box(80, 0, 130, 50))
294
295
print(f"Original polygon count: {region.count()}")
296
297
# Merge overlapping polygons
298
merged = region.merged()
299
print(f"After merge: {merged.count()}")
300
301
# Size operations
302
grown = merged.sized(10) # Grow by 10 units
303
shrunk = merged.sized(-5) # Shrink by 5 units
304
asymmetric = merged.sized(10, 5) # Grow 10 in X, 5 in Y
305
306
print(f"Original area: {merged.area()}")
307
print(f"Grown area: {grown.area()}")
308
print(f"Shrunk area: {shrunk.area()}")
309
```
310
311
### Design Rule Checking
312
313
```python
314
import klayout.db as db
315
316
# Load layout
317
layout = db.Layout()
318
layout.read("design.gds")
319
320
# Get shapes on metal layer
321
metal_layer = layout.layer(db.LayerInfo(1, 0))
322
metal_region = db.Region()
323
324
for cell in layout.each_cell():
325
if cell:
326
for shape in cell.shapes(metal_layer).each():
327
metal_region.insert(shape.polygon)
328
329
# Perform DRC checks
330
min_width = 100 # 100 nm minimum width
331
min_space = 120 # 120 nm minimum spacing
332
333
# Width check
334
width_violations = metal_region.width_check(min_width)
335
print(f"Width violations: {width_violations.count()}")
336
337
# Space check
338
space_violations = metal_region.space_check(min_space)
339
print(f"Space violations: {space_violations.count()}")
340
341
# Create error layer for violations
342
if width_violations.count() > 0:
343
error_region = width_violations.polygons(50) # 50nm marker around violations
344
# Insert error_region into layout for visualization
345
```
346
347
### Advanced Shape Selection
348
349
```python
350
import klayout.db as db
351
352
# Create test regions
353
shapes = db.Region()
354
shapes.insert(db.Box(0, 0, 100, 100)) # Large square
355
shapes.insert(db.Box(200, 0, 250, 250)) # Tall rectangle
356
shapes.insert(db.Box(300, 0, 400, 50)) # Wide rectangle
357
358
reference = db.Region()
359
reference.insert(db.Box(50, 50, 150, 150)) # Overlapping area
360
361
# Selection operations
362
interacting = shapes.select_interacting(reference)
363
print(f"Shapes interacting with reference: {interacting.count()}")
364
365
not_interacting = shapes.select_not_interacting(reference)
366
print(f"Shapes not interacting: {not_interacting.count()}")
367
368
overlapping = shapes.select_overlapping(reference)
369
print(f"Shapes overlapping reference: {overlapping.count()}")
370
371
# Area-based selection
372
large_shapes = db.Region()
373
for poly in shapes.each():
374
if poly.area() > 5000: # Only shapes larger than 5000 sq units
375
large_shapes.insert(poly)
376
377
print(f"Large shapes: {large_shapes.count()}")
378
```
379
380
### Working with Edges
381
382
```python
383
import klayout.db as db
384
385
# Create region and extract edges
386
region = db.Region()
387
region.insert(db.Box(0, 0, 100, 200))
388
region.insert(db.Box(100, 0, 200, 100))
389
390
# Get all edges
391
all_edges = region.edges()
392
print(f"Total edges: {all_edges.count()}")
393
print(f"Total edge length: {all_edges.length()}")
394
395
# Filter edges by length
396
long_edges = all_edges.with_length(100, None) # Edges >= 100 units
397
short_edges = all_edges.with_length(0, 99) # Edges < 100 units
398
399
print(f"Long edges: {long_edges.count()}")
400
print(f"Short edges: {short_edges.count()}")
401
402
# Filter by angle (horizontal and vertical)
403
horizontal = all_edges.with_angle(-1, 1) # ~0 degrees
404
vertical = all_edges.with_angle(89, 91) # ~90 degrees
405
406
print(f"Horizontal edges: {horizontal.count()}")
407
print(f"Vertical edges: {vertical.count()}")
408
```
409
410
### Corner Rounding and Smoothing
411
412
```python
413
import klayout.db as db
414
415
# Create region with sharp corners
416
region = db.Region()
417
# Create L-shaped polygon with sharp internal corner
418
points = [
419
db.Point(0, 0), db.Point(100, 0), db.Point(100, 50),
420
db.Point(50, 50), db.Point(50, 100), db.Point(0, 100)
421
]
422
region.insert(db.Polygon(points))
423
424
# Round corners
425
rounded = region.rounded_corners(
426
radius_inner=10, # Inner corner radius
427
radius_outer=15, # Outer corner radius
428
num_points=16 # Points per quarter circle
429
)
430
431
# Smooth small features
432
smoothed = region.smoothed(5) # Remove features smaller than 5 units
433
434
print(f"Original vertices: {sum(1 for _ in region.each())}")
435
print(f"After rounding: {sum(1 for _ in rounded.each())}")
436
```
437
438
### Hierarchical Processing
439
440
```python
441
import klayout.db as db
442
443
# Load hierarchical layout
444
layout = db.Layout()
445
layout.read("hierarchical_design.gds")
446
447
# Process specific layer across hierarchy
448
layer = layout.layer(db.LayerInfo(1, 0))
449
450
# Option 1: Flatten and process
451
flattened_region = db.Region()
452
for cell in layout.each_cell():
453
if cell:
454
# Get shapes with hierarchy context
455
shapes = cell.shapes(layer)
456
for shape in shapes.each():
457
flattened_region.insert(shape.polygon)
458
459
# Option 2: Use deep shapes (preserves hierarchy)
460
if hasattr(layout, 'top_cell') and layout.top_cell():
461
deep_shapes = db.DeepShapes(layout.top_cell())
462
hierarchical_region = deep_shapes.region(layer)
463
464
# Process while maintaining hierarchy information
465
processed = hierarchical_region.sized(10).merged()
466
```
467
468
### Complex DRC Rule Implementation
469
470
```python
471
import klayout.db as db
472
473
def check_enclosure_rule(inner_layer: db.Region, outer_layer: db.Region,
474
min_enclosure: int) -> db.EdgePairs:
475
"""
476
Check enclosure rule: outer_layer must enclose inner_layer by min_enclosure.
477
478
Parameters:
479
- inner_layer: Region that should be enclosed
480
- outer_layer: Region that should provide enclosure
481
- min_enclosure: Minimum enclosure distance
482
483
Returns:
484
EdgePairs: Enclosure violations
485
"""
486
# Areas where inner extends beyond outer (not enclosed)
487
not_enclosed = inner_layer - outer_layer
488
489
# Areas where enclosure is insufficient
490
grown_inner = inner_layer.sized(min_enclosure)
491
insufficient_enclosure = grown_inner - outer_layer
492
493
# Convert violations to edge pairs for reporting
494
violations = db.EdgePairs()
495
496
# Process each violation area
497
for poly in insufficient_enclosure.each():
498
# Find nearest edges between inner and outer
499
inner_edges = inner_layer.edges()
500
outer_edges = outer_layer.edges()
501
502
# This is simplified - real implementation would find closest edge pairs
503
# For each inner edge, find closest outer edge and check distance
504
505
return violations
506
507
# Example usage
508
via_layer = db.Region()
509
metal_layer = db.Region()
510
511
# Load actual shapes...
512
# via_layer.insert(...)
513
# metal_layer.insert(...)
514
515
enclosure_violations = check_enclosure_rule(via_layer, metal_layer, 50)
516
print(f"Enclosure violations: {enclosure_violations.count()}")
517
```