/// /// Functionality for reading/writing elements (geometry, text labels, /// structure references) and associated properties. /// use records::{BOX, BOUNDARY, NODE, PATH, TEXT, SREF, AREF, DATATYPE, PATHTYPE, BOXTYPE, NODETYPE, TEXTTYPE, LAYER, XY, WIDTH, COLROW, PRESENTATION, STRING, STRANS, MAG, ANGLE, PROPATTR, PROPVALUE, ENDEL, BGNEXTN, ENDEXTN, SNAME, }; use records; use record::{RecordHeader, Record}; use basic::{OResult, IResult, fail}; use std::collections::HashMap; use std::io::Write; /// /// Read element properties. /// /// Assumes PROPATTR records have unique values. /// Stops reading after consuming ENDEL record. /// /// Args: /// stream: Stream to read from. /// /// Returns: /// propattr: -> propvalue mapping /// pub fn read_properties(input: &[u8]) -> IResult>> { let mut properties = HashMap::new(); let (mut input, mut header) = RecordHeader::read(input)?; while header.tag != ENDEL::tag() { if header.tag == PROPATTR::tag() { let result = PROPATTR::read_data(input, header.data_size)?; input = result.0; let key = result.1; let result = PROPVALUE::read(input)?; input = result.0; let value = result.1; assert!(!properties.contains_key(&key), "Duplicate property key: {}", key); properties.insert(key, value); } (input, header) = RecordHeader::read(input)?; } Ok((input, properties)) } /// /// Write element properties. /// /// This is does _not_ write the ENDEL record. /// /// Args: /// stream: Stream to write to. /// pub fn write_properties(ww: &mut W, properties: &HashMap::>) -> OResult { let mut size = 0; for (key, value) in properties { size += PROPATTR::write(ww, key)?; size += PROPVALUE::write(ww, value)?; } Ok(size) } pub trait Element { /// /// Read from a stream to construct this object. /// Consumes up to (and including) the ENDEL record. /// fn read(input: &[u8]) -> IResult where Self: Sized; /// /// Write this element to a stream. /// Finishes with an ENDEL record. /// fn write(&self, ww: &mut W) -> OResult; } /// /// Datastructure representing /// an instance of a structure (SREF / structure reference) or /// an array of instances (AREF / array reference). /// Type is determined by the presence of the `colrow` tuple. /// /// Transforms are applied to each individual instance (_not_ /// to the instance's origin location || array vectors). /// #[derive(Debug, Clone)] pub struct Reference { /// Name of the structure being referenced. struct_name: Vec, /// Whether to mirror the pattern (negate y-values / flip across x-axis). Default false. invert_y: bool, /// Scaling factor (default 1) mag: f64, /// Rotation (degrees counterclockwise) angle_deg: f64, /// (For SREF) Location in the parent structure corresponding to the instance's origin (0, 0). /// (For AREF) 3 locations: /// [`offset`, /// `offset + col_basis_vector * colrow[0]`, /// `offset + row_basis_vector * colrow[1]`] /// which define the first instance's offset and the array's basis vectors. /// Note that many GDS implementations only support manhattan basis vectors, and some /// assume a certain axis mapping (e.g. x->columns, y->rows) and "reinterpret" the /// basis vectors to match it. xy: Vec, /// Number of columns and rows (AREF) || None (SREF) colrow: Option<(i16, i16)>, /// Properties associated with this reference. properties: HashMap::>, } impl Element for Reference { fn read(input: &[u8]) -> IResult { let mut invert_y = false; let mut mag = 1.0; let mut angle_deg = 0.0; let mut colrow = None; let (input, struct_name) = SNAME::skip_and_read(input)?; let (mut input, mut header) = RecordHeader::read(input)?; while header.tag != records::RTAG_XY { match header.tag { records::RTAG_STRANS => { let result = STRANS::read_data(input, header.data_size)?; input = result.0; invert_y = result.1[0]; }, records::RTAG_MAG => {(input, mag) = MAG::read_data(input, header.data_size)?;}, records::RTAG_ANGLE => {(input, angle_deg) = ANGLE::read_data(input, header.data_size)?;}, records::RTAG_COLROW => { let result = COLROW::read_data(input, header.data_size)?; input = result.0; colrow = Some((result.1[0], result.1[1])); }, _ => return fail(input, format!("Unexpected tag {:04x}", header.tag)), }; (input, header) = RecordHeader::read(input)?; } let (input, xy) = XY::read_data(input, header.data_size)?; let (input, properties) = read_properties(input)?; Ok((input, Reference{ struct_name: struct_name, xy: xy, properties: properties, colrow: colrow, invert_y: invert_y, mag: mag, angle_deg: angle_deg })) } fn write(&self, ww: &mut W) -> OResult { let mut size = 0; size += match self.colrow { None => SREF::write(ww, &())?, Some(_) => AREF::write(ww, &())?, }; size += SNAME::write(ww, &self.struct_name)?; if self.angle_deg != 0.0 || self.mag != 1.0 || self.invert_y { let strans = { let mut arr = [false; 16]; arr[0] = self.invert_y; arr }; size += STRANS::write(ww, &strans)?; if self.mag != 1.0 { size += MAG::write(ww, &self.mag)?; } if self.angle_deg != 0.0 { size += ANGLE::write(ww, &self.angle_deg)?; } } if let Some(cr) = self.colrow { size += COLROW::write(ww, &vec!{cr.0, cr.1})?; } size += XY::write(ww, &self.xy)?; size += write_properties(ww, &self.properties)?; size += ENDEL::write(ww, &())?; Ok(size) } } impl Reference { pub fn check(&self) { if self.colrow.is_some() { assert!(self.xy.len() != 6, "colrow is Some, so expected size-6 xy. Got {:?}", self.xy); } else { assert!(self.xy.len() != 2, "Expected size-2 xy. Got {:?}", self.xy); } } } /// /// Datastructure representing a Boundary element. /// #[derive(Debug, Clone)] pub struct Boundary { /// (layer, data_type) tuple layer: (i16, i16), /// Ordered vertices of the shape. First and last points should be identical. Order x0, y0, x1,... xy: Vec, /// Properties for the element. properties: HashMap::>, } impl Element for Boundary { fn read(input: &[u8]) -> IResult { let (input, layer) = LAYER::skip_and_read(input)?; let (input, dtype) = DATATYPE::read(input)?; let (input, xy) = XY::read(input)?; let (input, properties) = read_properties(input)?; Ok((input, Boundary{ layer: (layer, dtype), xy: xy, properties: properties, })) } fn write(&self, ww: &mut W) -> OResult { let mut size = 0; size += BOUNDARY::write(ww, &())?; size += LAYER::write(ww, &self.layer.0)?; size += DATATYPE::write(ww, &self.layer.1)?; size += XY::write(ww, &self.xy)?; size += write_properties(ww, &self.properties)?; size += ENDEL::write(ww, &())?; Ok(size) } } /// /// Datastructure representing a Path element. /// /// If `path_type < 4`, `extension` values are not written. /// During read, `exension` defaults to (0, 0) even if unused. /// #[derive(Debug, Clone)] pub struct Path { /// (layer, data_type) tuple layer: (i16, i16), /// End-cap type (0: flush, 1: circle, 2: square, 4: custom) path_type: i16, /// Path width width: i32, /// Extension when using path_type=4. Ignored otherwise. extension: (i32, i32), /// Path centerline coordinates. [x0, y0, x1, y1,...] xy: Vec, /// Properties for the element. properties: HashMap::>, } impl Element for Path { fn read(input: &[u8]) -> IResult { let mut path_type = 0; let mut width = 0; let mut bgn_ext = 0; let mut end_ext = 0; let (input, layer) = LAYER::skip_and_read(input)?; let (input, dtype) = DATATYPE::read(input)?; let (mut input, mut header) = RecordHeader::read(&input)?; while header.tag != records::RTAG_XY { match header.tag { records::RTAG_PATHTYPE => {(input, path_type) = PATHTYPE::read_data(input, header.data_size)?;}, records::RTAG_WIDTH => {(input, width) = WIDTH::read_data(input, header.data_size)?;}, records::RTAG_BGNEXTN => {(input, bgn_ext) = BGNEXTN::read_data(input, header.data_size)?;}, records::RTAG_ENDEXTN => {(input, end_ext) = ENDEXTN::read_data(input, header.data_size)?;}, _ => return fail(input, format!("Unexpected tag {:04x}", header.tag)), }; (input, header) = RecordHeader::read(&input)?; } let (input, xy) = XY::read_data(input, header.data_size)?; let (input, properties) = read_properties(input)?; Ok((input, Path{ layer: (layer, dtype), xy: xy, properties: properties, extension: (bgn_ext, end_ext), path_type: path_type, width: width, })) } fn write(&self, ww: &mut W) -> OResult { let mut size = 0; size += PATH::write(ww, &())?; size += LAYER::write(ww, &self.layer.0)?; size += DATATYPE::write(ww, &self.layer.1)?; if self.path_type != 0 { size += PATHTYPE::write(ww, &self.path_type)?; } if self.width != 0 { size += WIDTH::write(ww, &self.width)?; } if self.path_type < 4 { let (bgn_ext, end_ext) = self.extension; if bgn_ext != 0 { size += BGNEXTN::write(ww, &bgn_ext)?; } if end_ext != 0 { size += ENDEXTN::write(ww, &end_ext)?; } } size += XY::write(ww, &self.xy)?; size += write_properties(ww, &self.properties)?; size += ENDEL::write(ww, &())?; Ok(size) } } /// /// Datastructure representing a Box element. Rarely used. /// #[derive(Debug, Clone)] pub struct GDSBox { /// (layer, box_type) tuple layer: (i16, i16), /// Box coordinates (5 pairs) xy: Vec, /// Properties for the element. properties: HashMap::>, } impl Element for GDSBox { fn read(input: &[u8]) -> IResult { let (input, layer) = LAYER::skip_and_read(input)?; let (input, dtype) = BOXTYPE::read(input)?; let (input, xy) = XY::read(input)?; let (input, properties) = read_properties(input)?; Ok((input, GDSBox{ layer: (layer, dtype), xy: xy, properties: properties, })) } fn write(&self, ww: &mut W) -> OResult { let mut size = 0; size += BOX::write(ww, &())?; size += LAYER::write(ww, &self.layer.0)?; size += BOXTYPE::write(ww, &self.layer.1)?; size += XY::write(ww, &self.xy)?; size += write_properties(ww, &self.properties)?; size += ENDEL::write(ww, &())?; Ok(size) } } /// /// Datastructure representing a Node element. Rarely used. /// #[derive(Debug, Clone)] pub struct Node { /// (layer, box_type) tuple layer: (i16, i16), /// 1-50 pairs of coordinates. xy: Vec, /// Properties for the element. properties: HashMap::>, } impl Element for Node { fn read(input: &[u8]) -> IResult { let (input, layer) = LAYER::skip_and_read(input)?; let (input, dtype) = NODETYPE::read(input)?; let (input, xy) = XY::read(input)?; let (input, properties) = read_properties(input)?; Ok((input, Node{ layer: (layer, dtype), xy: xy, properties: properties, })) } fn write(&self, ww: &mut W) -> OResult { let mut size = 0; size += NODE::write(ww, &())?; size += LAYER::write(ww, &self.layer.0)?; size += NODETYPE::write(ww, &self.layer.1)?; size += XY::write(ww, &self.xy)?; size += write_properties(ww, &self.properties)?; size += ENDEL::write(ww, &())?; Ok(size) } } /// /// Datastructure representing a text label. /// #[derive(Debug, Clone)] pub struct Text { /// (layer, node_type) tuple layer: (i16, i16), /// Bit array. Default all zeros. /// bits 0-1: 00 left/01 center/10 right /// bits 2-3: 00 top/01 middle/10 bottom /// bits 4-5: font number presentation: [bool; 16], /// Default 0 path_type: i16, /// Default 0 width: i32, /// Vertical inversion. Default false. invert_y: bool, /// Scaling factor. Default 1. mag: f64, /// Rotation (ccw). Default 0. angle_deg: f64, /// Position (1 pair only) xy: Vec, /// Text content string: Vec, /// Properties for the element. properties: HashMap::> } impl Element for Text { fn read(input: &[u8]) -> IResult { let mut path_type = 0; let mut presentation = [false; 16]; let mut invert_y = false; let mut width = 0; let mut mag = 1.0; let mut angle_deg = 0.0; let (input, layer) = LAYER::skip_and_read(input)?; let (input, dtype) = TEXTTYPE::read(input)?; let (mut input, mut header) = RecordHeader::read(input)?; while header.tag != records::RTAG_XY { match header.tag { records::RTAG_PRESENTATION => {(input, presentation) = PRESENTATION::read_data(input, header.data_size)?;}, records::RTAG_PATHTYPE => {(input, path_type) = PATHTYPE::read_data(input, header.data_size)?;}, records::RTAG_WIDTH => {(input, width) = WIDTH::read_data(input, header.data_size)?;}, records::RTAG_STRANS => { let result = STRANS::read_data(input, header.data_size)?; input = result.0; invert_y = result.1[0]; }, records::RTAG_MAG => {(input, mag) = MAG::read_data(input, header.data_size)?;}, records::RTAG_ANGLE => {(input, angle_deg) = ANGLE::read_data(input, header.data_size)?;}, _ => return fail(input, format!("Unexpected tag {:04x}", header.tag)), } (input, header) = RecordHeader::read(input)?; } let (input, xy) = XY::read_data(input, header.data_size)?; let (input, string) = STRING::read(input)?; let (input, properties) = read_properties(input)?; Ok((input, Text{ layer: (layer, dtype), xy: xy, properties: properties, string: string, presentation: presentation, path_type: path_type, width: width, invert_y: invert_y, mag: mag, angle_deg: angle_deg, })) } fn write(&self, ww: &mut W) -> OResult { let mut size = 0; size += TEXT::write(ww, &())?; size += LAYER::write(ww, &self.layer.0)?; size += TEXTTYPE::write(ww, &self.layer.1)?; if self.presentation.iter().any(|&x| x) { size += PRESENTATION::write(ww, &self.presentation)?; } if self.path_type != 0 { size += PATHTYPE::write(ww, &self.path_type)?; } if self.width != 0 { size += WIDTH::write(ww, &self.width)? } if self.angle_deg != 0.0 || self.mag != 1.0 || self.invert_y { let strans = { let mut arr = [false; 16]; arr[0] = self.invert_y; arr }; size += STRANS::write(ww, &strans)?; if self.mag != 1.0 { size += MAG::write(ww, &self.mag)?; } if self.angle_deg != 0.0 { size += ANGLE::write(ww, &self.angle_deg)?; } } size += XY::write(ww, &self.xy)?; size += STRING::write(ww, &self.string)?; size += write_properties(ww, &self.properties)?; size += ENDEL::write(ww, &())?; Ok(size) } }