CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-klayout

KLayout is a high performance layout viewer and editor that supports GDS and OASIS files and more formats

Pending
Overview
Eval results
Files

shape-operations.mddocs/

Shape Collections and Boolean Operations

Advanced geometric operations including boolean operations (AND, OR, XOR, NOT), sizing, merging, and comprehensive region analysis for layout verification and processing.

Capabilities

Region Operations

The Region class provides powerful bulk operations on collections of polygons, essential for design rule checking and layout processing.

class Region:
    def __init__(self, shapes=None):
        """
        Create a region from shapes or empty region.
        
        Parameters:
        - shapes: Initial shapes (Polygon, Box, or iterable of shapes)
        """
    
    def insert(self, shape) -> None:
        """
        Insert a shape into the region.
        
        Parameters:
        - shape: Polygon, Box, or other geometric shape
        """
    
    def __and__(self, other: Region) -> Region:
        """Boolean AND operation (intersection)."""
    
    def __or__(self, other: Region) -> Region:
        """Boolean OR operation (union)."""
    
    def __xor__(self, other: Region) -> Region:
        """Boolean XOR operation (exclusive or)."""
    
    def __sub__(self, other: Region) -> Region:
        """Boolean subtraction (difference)."""
    
    def sized(self, dx: int, dy: int = None) -> Region:
        """
        Size operation (grow/shrink polygons).
        
        Parameters:
        - dx: X sizing amount (positive to grow)
        - dy: Y sizing amount (defaults to dx)
        
        Returns:
        Region: Sized region
        """
    
    def merged(self) -> Region:
        """Merge overlapping and touching polygons."""
    
    def smoothed(self, d: int) -> Region:
        """
        Smooth region by removing small notches and gaps.
        
        Parameters:
        - d: Smoothing distance
        """
    
    def rounded_corners(self, radius_inner: int, radius_outer: int, num_points: int) -> Region:
        """
        Round corners of polygons.
        
        Parameters:
        - radius_inner: Inner corner radius
        - radius_outer: Outer corner radius  
        - num_points: Points per quarter circle
        """
    
    def area(self) -> int:
        """Calculate total area of all polygons."""
    
    def perimeter(self) -> int:
        """Calculate total perimeter of all polygons."""
    
    def bbox(self) -> Box:
        """Get bounding box of entire region."""
    
    def is_empty(self) -> bool:
        """Check if region contains no shapes."""

    def count(self) -> int:
        """Get number of polygons in region."""
    
    def each(self):
        """Iterate over polygons in region."""
    
    def select_interacting(self, other: Region) -> Region:
        """Select polygons that interact with other region."""
    
    def select_not_interacting(self, other: Region) -> Region:
        """Select polygons that don't interact with other region."""
    
    def select_overlapping(self, other: Region) -> Region:
        """Select polygons that overlap with other region."""
    
    def select_not_overlapping(self, other: Region) -> Region:
        """Select polygons that don't overlap with other region."""
    
    def width_check(self, d: int) -> EdgePairs:
        """
        Check for width violations.
        
        Parameters:
        - d: Minimum width
        
        Returns:
        EdgePairs: Width violation edge pairs
        """
    
    def space_check(self, d: int) -> EdgePairs:
        """
        Check for spacing violations.
        
        Parameters:
        - d: Minimum spacing
        
        Returns:
        EdgePairs: Spacing violation edge pairs
        """

Edge Collections

class Edges:
    def __init__(self, shapes=None):
        """
        Create edge collection from shapes.
        
        Parameters:
        - shapes: Initial shapes or edges
        """
    
    def insert(self, edge: Edge) -> None:
        """Insert an edge into the collection."""
    
    def merged(self) -> Edges:
        """Merge connected and overlapping edges."""
    
    def extended(self, ext_begin: int, ext_end: int) -> Edges:
        """
        Extend edges at both ends.
        
        Parameters:
        - ext_begin: Extension at beginning
        - ext_end: Extension at end
        """
    
    def length(self) -> int:
        """Get total length of all edges."""
    
    def bbox(self) -> Box:
        """Get bounding box of all edges."""
    
    def each(self):
        """Iterate over edges."""
    
    def with_length(self, min_length: int, max_length: int = None) -> Edges:
        """
        Select edges within length range.
        
        Parameters:
        - min_length: Minimum length
        - max_length: Maximum length (unlimited if None)
        """
    
    def with_angle(self, min_angle: float, max_angle: float) -> Edges:
        """Select edges within angle range (in degrees)."""

Edge Pairs (for DRC Results)

class EdgePairs:
    def __init__(self):
        """Create empty edge pair collection."""
    
    def insert(self, edge1: Edge, edge2: Edge) -> None:
        """
        Insert an edge pair.
        
        Parameters:
        - edge1: First edge
        - edge2: Second edge
        """
    
    def count(self) -> int:
        """Get number of edge pairs."""
    
    def each(self):
        """Iterate over edge pairs."""
    
    def polygons(self, enlargement: int = 0) -> Region:
        """
        Convert edge pairs to polygons for visualization.
        
        Parameters:
        - enlargement: Polygon enlargement around edges
        
        Returns:
        Region: Region representing the edge pairs
        """
    
    def first_edges(self) -> Edges:
        """Get collection of first edges from all pairs."""
    
    def second_edges(self) -> Edges:
        """Get collection of second edges from all pairs."""

Text Collections

class Texts:
    def __init__(self, shapes=None):
        """Create text collection."""
    
    def insert(self, text: Text) -> None:
        """Insert a text object."""
    
    def each(self):
        """Iterate over text objects."""
    
    def count(self) -> int:
        """Get number of text objects."""
    
    def bbox(self) -> Box:
        """Get bounding box of all texts."""
    
    def with_text(self, pattern: str) -> Texts:
        """
        Select texts matching pattern.
        
        Parameters:
        - pattern: Text pattern to match
        """

Deep Region Operations

class DeepShapes:
    """Deep shapes for hierarchical operations."""
    
    def region(self, layer: int) -> Region:
        """Get region for layer with hierarchy flattening."""
    
    def edges(self, layer: int) -> Edges:
        """Get edges for layer with hierarchy flattening."""

Usage Examples

Basic Boolean Operations

import klayout.db as db

# Create two regions
region1 = db.Region()
region1.insert(db.Box(0, 0, 100, 100))
region1.insert(db.Box(50, 50, 150, 150))

region2 = db.Region()
region2.insert(db.Box(25, 25, 75, 75))
region2.insert(db.Box(125, 25, 175, 75))

# Boolean operations
intersection = region1 & region2  # AND
union = region1 | region2         # OR
difference = region1 - region2    # SUBTRACT
symmetric_diff = region1 ^ region2 # XOR

print(f"Region1 area: {region1.area()}")
print(f"Region2 area: {region2.area()}")
print(f"Intersection area: {intersection.area()}")
print(f"Union area: {union.area()}")

Sizing and Merging Operations

import klayout.db as db

# Create region with overlapping shapes
region = db.Region()
region.insert(db.Box(0, 0, 50, 50))
region.insert(db.Box(40, 0, 90, 50))
region.insert(db.Box(80, 0, 130, 50))

print(f"Original polygon count: {region.count()}")

# Merge overlapping polygons
merged = region.merged()
print(f"After merge: {merged.count()}")

# Size operations
grown = merged.sized(10)      # Grow by 10 units
shrunk = merged.sized(-5)     # Shrink by 5 units
asymmetric = merged.sized(10, 5)  # Grow 10 in X, 5 in Y

print(f"Original area: {merged.area()}")
print(f"Grown area: {grown.area()}")
print(f"Shrunk area: {shrunk.area()}")

Design Rule Checking

import klayout.db as db

# Load layout
layout = db.Layout()
layout.read("design.gds")

# Get shapes on metal layer
metal_layer = layout.layer(db.LayerInfo(1, 0))
metal_region = db.Region()

for cell in layout.each_cell():
    if cell:
        for shape in cell.shapes(metal_layer).each():
            metal_region.insert(shape.polygon)

# Perform DRC checks
min_width = 100    # 100 nm minimum width
min_space = 120    # 120 nm minimum spacing

# Width check
width_violations = metal_region.width_check(min_width)
print(f"Width violations: {width_violations.count()}")

# Space check  
space_violations = metal_region.space_check(min_space)
print(f"Space violations: {space_violations.count()}")

# Create error layer for violations
if width_violations.count() > 0:
    error_region = width_violations.polygons(50)  # 50nm marker around violations
    # Insert error_region into layout for visualization

Advanced Shape Selection

import klayout.db as db

# Create test regions
shapes = db.Region()
shapes.insert(db.Box(0, 0, 100, 100))      # Large square
shapes.insert(db.Box(200, 0, 250, 250))    # Tall rectangle
shapes.insert(db.Box(300, 0, 400, 50))     # Wide rectangle

reference = db.Region()
reference.insert(db.Box(50, 50, 150, 150)) # Overlapping area

# Selection operations
interacting = shapes.select_interacting(reference)
print(f"Shapes interacting with reference: {interacting.count()}")

not_interacting = shapes.select_not_interacting(reference)
print(f"Shapes not interacting: {not_interacting.count()}")

overlapping = shapes.select_overlapping(reference)
print(f"Shapes overlapping reference: {overlapping.count()}")

# Area-based selection
large_shapes = db.Region()
for poly in shapes.each():
    if poly.area() > 5000:  # Only shapes larger than 5000 sq units
        large_shapes.insert(poly)

print(f"Large shapes: {large_shapes.count()}")

Working with Edges

import klayout.db as db

# Create region and extract edges
region = db.Region()
region.insert(db.Box(0, 0, 100, 200))
region.insert(db.Box(100, 0, 200, 100))

# Get all edges
all_edges = region.edges()
print(f"Total edges: {all_edges.count()}")
print(f"Total edge length: {all_edges.length()}")

# Filter edges by length
long_edges = all_edges.with_length(100, None)  # Edges >= 100 units
short_edges = all_edges.with_length(0, 99)     # Edges < 100 units

print(f"Long edges: {long_edges.count()}")
print(f"Short edges: {short_edges.count()}")

# Filter by angle (horizontal and vertical)
horizontal = all_edges.with_angle(-1, 1)       # ~0 degrees
vertical = all_edges.with_angle(89, 91)        # ~90 degrees

print(f"Horizontal edges: {horizontal.count()}")
print(f"Vertical edges: {vertical.count()}")

Corner Rounding and Smoothing

import klayout.db as db

# Create region with sharp corners
region = db.Region()
# Create L-shaped polygon with sharp internal corner
points = [
    db.Point(0, 0), db.Point(100, 0), db.Point(100, 50),
    db.Point(50, 50), db.Point(50, 100), db.Point(0, 100)
]
region.insert(db.Polygon(points))

# Round corners
rounded = region.rounded_corners(
    radius_inner=10,  # Inner corner radius
    radius_outer=15,  # Outer corner radius
    num_points=16     # Points per quarter circle
)

# Smooth small features
smoothed = region.smoothed(5)  # Remove features smaller than 5 units

print(f"Original vertices: {sum(1 for _ in region.each())}")
print(f"After rounding: {sum(1 for _ in rounded.each())}")

Hierarchical Processing

import klayout.db as db

# Load hierarchical layout
layout = db.Layout()
layout.read("hierarchical_design.gds")

# Process specific layer across hierarchy
layer = layout.layer(db.LayerInfo(1, 0))

# Option 1: Flatten and process
flattened_region = db.Region()
for cell in layout.each_cell():
    if cell:
        # Get shapes with hierarchy context
        shapes = cell.shapes(layer)
        for shape in shapes.each():
            flattened_region.insert(shape.polygon)

# Option 2: Use deep shapes (preserves hierarchy)
if hasattr(layout, 'top_cell') and layout.top_cell():
    deep_shapes = db.DeepShapes(layout.top_cell())
    hierarchical_region = deep_shapes.region(layer)
    
    # Process while maintaining hierarchy information
    processed = hierarchical_region.sized(10).merged()

Complex DRC Rule Implementation

import klayout.db as db

def check_enclosure_rule(inner_layer: db.Region, outer_layer: db.Region, 
                        min_enclosure: int) -> db.EdgePairs:
    """
    Check enclosure rule: outer_layer must enclose inner_layer by min_enclosure.
    
    Parameters:
    - inner_layer: Region that should be enclosed
    - outer_layer: Region that should provide enclosure
    - min_enclosure: Minimum enclosure distance
    
    Returns:
    EdgePairs: Enclosure violations
    """
    # Areas where inner extends beyond outer (not enclosed)
    not_enclosed = inner_layer - outer_layer
    
    # Areas where enclosure is insufficient
    grown_inner = inner_layer.sized(min_enclosure)
    insufficient_enclosure = grown_inner - outer_layer
    
    # Convert violations to edge pairs for reporting
    violations = db.EdgePairs()
    
    # Process each violation area
    for poly in insufficient_enclosure.each():
        # Find nearest edges between inner and outer
        inner_edges = inner_layer.edges()
        outer_edges = outer_layer.edges()
        
        # This is simplified - real implementation would find closest edge pairs
        # For each inner edge, find closest outer edge and check distance
        
    return violations

# Example usage
via_layer = db.Region()
metal_layer = db.Region()

# Load actual shapes...
# via_layer.insert(...)
# metal_layer.insert(...)

enclosure_violations = check_enclosure_rule(via_layer, metal_layer, 50)
print(f"Enclosure violations: {enclosure_violations.count()}")

Install with Tessl CLI

npx tessl i tessl/pypi-klayout

docs

database-operations.md

file-io.md

hierarchical-design.md

index.md

layout-viewer.md

shape-operations.md

transformations.md

verification.md

tile.json