Working with Elements

Learn how to work with different element types in LayKit.

Counting Elements

Count different element types in a GDSII file:

use laykit::{GDSIIFile, GDSElement};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let gds = GDSIIFile::read_from_file("design.gds")?;
    
    let mut counts = ElementCounts::default();
    
    for structure in &gds.structures {
        for element in &structure.elements {
            match element {
                GDSElement::Boundary(_) => counts.boundaries += 1,
                GDSElement::Path(_) => counts.paths += 1,
                GDSElement::Text(_) => counts.texts += 1,
                GDSElement::StructRef(_) => counts.refs += 1,
                GDSElement::ArrayRef(_) => counts.arrays += 1,
                GDSElement::Node(_) => counts.nodes += 1,
                GDSElement::Box(_) => counts.boxes += 1,
            }
        }
    }
    
    println!("Element counts:");
    println!("  Boundaries: {}", counts.boundaries);
    println!("  Paths: {}", counts.paths);
    println!("  Texts: {}", counts.texts);
    println!("  References: {}", counts.refs);
    println!("  Arrays: {}", counts.arrays);
    println!("  Nodes: {}", counts.nodes);
    println!("  Boxes: {}", counts.boxes);
    
    Ok(())
}

#[derive(Default)]
struct ElementCounts {
    boundaries: usize,
    paths: usize,
    texts: usize,
    refs: usize,
    arrays: usize,
    nodes: usize,
    boxes: usize,
}

Filtering by Layer

Extract all elements on a specific layer:

#![allow(unused)]
fn main() {
use laykit::{GDSIIFile, GDSElement};

fn filter_by_layer(gds: &GDSIIFile, target_layer: i16) {
    for structure in &gds.structures {
        let mut count = 0;
        
        for element in &structure.elements {
            let layer = match element {
                GDSElement::Boundary(b) => Some(b.layer),
                GDSElement::Path(p) => Some(p.layer),
                GDSElement::Text(t) => Some(t.layer),
                GDSElement::Node(n) => Some(n.layer),
                GDSElement::Box(b) => Some(b.layer),
                _ => None,
            };
            
            if layer == Some(target_layer) {
                count += 1;
            }
        }
        
        if count > 0 {
            println!("{}: {} elements on layer {}", 
                structure.name, count, target_layer);
        }
    }
}
}

Calculating Bounding Box

Find the bounding box of all boundaries:

use laykit::{GDSIIFile, GDSElement};

fn calculate_bounds(gds: &GDSIIFile) -> Option<(i32, i32, i32, i32)> {
    let mut min_x = i32::MAX;
    let mut min_y = i32::MAX;
    let mut max_x = i32::MIN;
    let mut max_y = i32::MIN;
    let mut found = false;
    
    for structure in &gds.structures {
        for element in &structure.elements {
            if let GDSElement::Boundary(b) = element {
                for (x, y) in &b.xy {
                    min_x = min_x.min(*x);
                    min_y = min_y.min(*y);
                    max_x = max_x.max(*x);
                    max_y = max_y.max(*y);
                    found = true;
                }
            }
        }
    }
    
    if found {
        Some((min_x, min_y, max_x, max_y))
    } else {
        None
    }
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let gds = GDSIIFile::read_from_file("design.gds")?;
    
    if let Some((min_x, min_y, max_x, max_y)) = calculate_bounds(&gds) {
        println!("Bounding box:");
        println!("  Min: ({}, {})", min_x, min_y);
        println!("  Max: ({}, {})", max_x, max_y);
        println!("  Size: {} × {}", max_x - min_x, max_y - min_y);
    } else {
        println!("No boundaries found");
    }
    
    Ok(())
}

Modifying Elements

Change all elements on layer 1 to layer 2:

use laykit::{GDSIIFile, GDSElement};

fn remap_layer(gds: &mut GDSIIFile, from_layer: i16, to_layer: i16) {
    for structure in &mut gds.structures {
        for element in &mut structure.elements {
            match element {
                GDSElement::Boundary(b) if b.layer == from_layer => {
                    b.layer = to_layer;
                }
                GDSElement::Path(p) if p.layer == from_layer => {
                    p.layer = to_layer;
                }
                GDSElement::Text(t) if t.layer == from_layer => {
                    t.layer = to_layer;
                }
                _ => {}
            }
        }
    }
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut gds = GDSIIFile::read_from_file("input.gds")?;
    
    remap_layer(&mut gds, 1, 2);
    
    gds.write_to_file("remapped.gds")?;
    println!("✅ Remapped layer 1 to layer 2");
    
    Ok(())
}

Extracting Text Labels

Get all text labels from a design:

use laykit::{GDSIIFile, GDSElement};

fn extract_text_labels(gds: &GDSIIFile) -> Vec<String> {
    let mut labels = Vec::new();
    
    for structure in &gds.structures {
        for element in &structure.elements {
            if let GDSElement::Text(t) = element {
                labels.push(t.string.clone());
            }
        }
    }
    
    labels
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let gds = GDSIIFile::read_from_file("design.gds")?;
    let labels = extract_text_labels(&gds);
    
    println!("Found {} text labels:", labels.len());
    for (i, label) in labels.iter().enumerate() {
        println!("  {}. {}", i + 1, label);
    }
    
    Ok(())
}

Scaling Coordinates

Scale all coordinates by a factor:

use laykit::{GDSIIFile, GDSElement};

fn scale_design(gds: &mut GDSIIFile, factor: f64) {
    for structure in &mut gds.structures {
        for element in &mut structure.elements {
            match element {
                GDSElement::Boundary(b) => {
                    for (x, y) in &mut b.xy {
                        *x = (*x as f64 * factor) as i32;
                        *y = (*y as f64 * factor) as i32;
                    }
                }
                GDSElement::Path(p) => {
                    for (x, y) in &mut p.xy {
                        *x = (*x as f64 * factor) as i32;
                        *y = (*y as f64 * factor) as i32;
                    }
                    if let Some(width) = &mut p.width {
                        *width = (*width as f64 * factor) as i32;
                    }
                }
                GDSElement::Text(t) => {
                    t.xy.0 = (t.xy.0 as f64 * factor) as i32;
                    t.xy.1 = (t.xy.1 as f64 * factor) as i32;
                }
                _ => {}
            }
        }
    }
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut gds = GDSIIFile::read_from_file("input.gds")?;
    
    scale_design(&mut gds, 2.0); // 2x scaling
    
    gds.write_to_file("scaled.gds")?;
    println!("✅ Design scaled by 2x");
    
    Ok(())
}

Filtering Structures

Keep only structures matching a pattern:

use laykit::GDSIIFile;

fn filter_structures(gds: &mut GDSIIFile, pattern: &str) {
    gds.structures.retain(|s| s.name.contains(pattern));
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut gds = GDSIIFile::read_from_file("input.gds")?;
    
    println!("Before: {} structures", gds.structures.len());
    
    filter_structures(&mut gds, "TOP");
    
    println!("After: {} structures", gds.structures.len());
    
    gds.write_to_file("filtered.gds")?;
    println!("✅ Filtered structures");
    
    Ok(())
}

Merging Files

Combine multiple GDSII files:

use laykit::GDSIIFile;

fn merge_files(files: Vec<&str>) -> Result<GDSIIFile, Box<dyn std::error::Error>> {
    let mut merged = GDSIIFile::new("MERGED".to_string());
    merged.units = (1e-6, 1e-9);
    
    for file_path in files {
        let gds = GDSIIFile::read_from_file(file_path)?;
        
        // Copy units from first file
        if merged.structures.is_empty() {
            merged.units = gds.units;
        }
        
        // Add all structures
        for structure in gds.structures {
            merged.structures.push(structure);
        }
    }
    
    Ok(merged)
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let merged = merge_files(vec!["file1.gds", "file2.gds", "file3.gds"])?;
    
    println!("Merged {} structures", merged.structures.len());
    
    merged.write_to_file("merged.gds")?;
    println!("✅ Files merged");
    
    Ok(())
}