482 lines
16 KiB
Rust
482 lines
16 KiB
Rust
|
/*
|
||
|
* Functionality for reading/writing elements (geometry, text labels,
|
||
|
* structure references) and associated properties.
|
||
|
*/
|
||
|
|
||
|
//from .record import Record
|
||
|
//
|
||
|
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;
|
||
|
use basic::{OWResult};
|
||
|
|
||
|
use std::collections::HashMap;
|
||
|
use std::io::Write;
|
||
|
use nom::IResult;
|
||
|
|
||
|
|
||
|
pub fn read_properties(input: &[u8]) -> IResult<&[u8], HashMap::<i32, Vec<u8>>> {
|
||
|
/*
|
||
|
* 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
|
||
|
*/
|
||
|
let properties = HashMap{};
|
||
|
|
||
|
let (input, header) = RecordHeader::parse(input)?;
|
||
|
while header.tag != ENDEL::tag() {
|
||
|
if header.tag == PROPATTR::tag() {
|
||
|
let (input, key) = PROPATTR::read_data(input, header.data_size)?;
|
||
|
let (input, value) = PROPVALUE::read(input)?;
|
||
|
assert!(!properties.contains_key(key), format!{"Duplicate property key: {}", key});
|
||
|
properties.insert(key, value);
|
||
|
}
|
||
|
let (input, header) = RecordHeader::parse(input)?;
|
||
|
}
|
||
|
Ok((input, properties))
|
||
|
}
|
||
|
|
||
|
|
||
|
fn write_properties<W: Write>(ww: W, properties: &HashMap::<i32, Vec<u8>>) -> OWResult {
|
||
|
/*
|
||
|
* Write element properties.
|
||
|
*
|
||
|
* This is does _not_ write the ENDEL record.
|
||
|
*
|
||
|
* Args:
|
||
|
* stream: Stream to write to.
|
||
|
*/
|
||
|
let mut size = 0;
|
||
|
for (key, value) in &properties {
|
||
|
size += PROPATTR::write(ww, key)?;
|
||
|
size += PROPVALUE::write(ww, value)?;
|
||
|
}
|
||
|
Ok(size)
|
||
|
}
|
||
|
|
||
|
trait Element {
|
||
|
fn parse(input: &[u8]) -> Self;
|
||
|
/*
|
||
|
* Read from a stream to construct this object.
|
||
|
* Consumes up to (and including) the ENDEL record.
|
||
|
*/
|
||
|
|
||
|
fn write<W: Write>(&self, ww: W) -> OWResult;
|
||
|
/*
|
||
|
* Write this element to a stream.
|
||
|
* Finishes with an ENDEL record.
|
||
|
*/
|
||
|
}
|
||
|
|
||
|
struct Reference {
|
||
|
/*
|
||
|
* 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).
|
||
|
*/
|
||
|
struct_name: Vec<u8>, // Name of the structure being referenced.
|
||
|
invert_y: bool, // Whether to mirror the pattern (negate y-values / flip across x-axis). Default false.
|
||
|
mag: f64, // Scaling factor (default 1) """
|
||
|
angle_deg: f64, // Rotation (degrees counterclockwise)
|
||
|
xy: Vec<i32>,
|
||
|
/*
|
||
|
* (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.
|
||
|
*/
|
||
|
|
||
|
colrow: Option<(i32, i32)>, // Number of columns and rows (AREF) || None (SREF)
|
||
|
properties: HashMap::<i16, Vec<u8>>, // Properties associated with this reference.
|
||
|
}
|
||
|
|
||
|
impl Element for Reference {
|
||
|
fn parse(input: &[u8]) -> IResult<&[u8], Self> {
|
||
|
let mut invert_y = false;
|
||
|
let mut mag = 1;
|
||
|
let mut angle_deg = 0;
|
||
|
let mut colrow = None;
|
||
|
let (input, mut struct_name) = SNAME::skip_and_read(input)?;
|
||
|
|
||
|
let (input, mut header) = RecordHeader::parse(input)?;
|
||
|
while header.tag != records::RTAG_XY {
|
||
|
match header.tag {
|
||
|
records::RTAG_STRANS =>
|
||
|
{let (input, invert_y) = STRANS::read_data(input, header.data_size)?[0];},
|
||
|
records::RTAG_MAG =>
|
||
|
{let (input, mag) = MAG::read_data(input, header.data_size)?;},
|
||
|
records::RTAG_ANGLE =>
|
||
|
{let (input, angle_deg) = ANGLE::read_data(input, header.data_size)?;},
|
||
|
records::RTAG_COLROW =>
|
||
|
{let (input, colrow) = COLROW::read_data(input, header.data_size)?;},
|
||
|
_ =>
|
||
|
return Err(format!("Unexpected tag {:04x}", header.tag)),
|
||
|
};
|
||
|
let (input, header) = RecordHeader::parse(input)?;
|
||
|
}
|
||
|
let (input, xy) = XY::read_data(input, header.data_size)?;
|
||
|
let (input, properties) = read_properties(input)?;
|
||
|
Reference{
|
||
|
struct_name: struct_name,
|
||
|
xy: xy,
|
||
|
properties: properties,
|
||
|
colrow: colrow,
|
||
|
invert_y: invert_y,
|
||
|
mag: mag,
|
||
|
angle_deg: angle_deg
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fn write<W: Write>(&self, ww: W) -> OWResult {
|
||
|
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 || self.mag != 1 || self.invert_y {
|
||
|
size += STRANS::write(ww, (self.invert_y as u16) << 15)?;
|
||
|
if self.mag != 1 {
|
||
|
size += MAG::write(ww, self.mag)?;
|
||
|
}
|
||
|
if self.angle_deg !=0 {
|
||
|
size += ANGLE::write(ww, self.angle_deg)?;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if self.colrow.is_some() {
|
||
|
size += COLROW::write(ww, self.colrow)?;
|
||
|
}
|
||
|
|
||
|
size += XY::write(ww, self.xy)?;
|
||
|
size += write_properties(ww, self.properties)?;
|
||
|
size += ENDEL::write(ww, None)?;
|
||
|
Ok(size)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl Reference {
|
||
|
pub fn check(&self) {
|
||
|
if self.colrow.is_some() {
|
||
|
assert!(self.xy.len() != 6, format!("colrow is Some, so expected size-6 xy. Got {}", self.xy));
|
||
|
} else {
|
||
|
assert!(self.xy.len() != 2, format!("Expected size-2 xy. Got {}", self.xy));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
struct Boundary {
|
||
|
/*
|
||
|
* Datastructure representing a Boundary element.
|
||
|
*/
|
||
|
layer: (i16, i16), // (layer, data_type) tuple
|
||
|
xy: Vec<i32>, // Ordered vertices of the shape. First and last points should be identical. Order x0, y0, x1,...
|
||
|
properties: HashMap::<i16, Vec<u8>>, // Properties for the element.
|
||
|
}
|
||
|
|
||
|
impl Element for Boundary {
|
||
|
fn parse(input: &[u8]) -> IResult<&[u8], Self> {
|
||
|
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)?;
|
||
|
Boundary{
|
||
|
layer: (layer, dtype),
|
||
|
xy: xy,
|
||
|
properties: properties,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fn write<W: Write>(&self, ww: W) -> OWResult {
|
||
|
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)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
struct Path {
|
||
|
/*
|
||
|
* Datastructure representing a Path element.
|
||
|
|
||
|
* If `path_type < 4`, `extension` values are not written.
|
||
|
* During read, `exension` defaults to (0, 0) even if unused.
|
||
|
*/
|
||
|
layer: (i16, i16), // (layer, data_type) tuple
|
||
|
path_type: i16, // End-cap type (0: flush, 1: circle, 2: square, 4: custom)
|
||
|
width: i16, // Path width
|
||
|
extension: (i32, i32), // Extension when using path_type=4. Ignored otherwise.
|
||
|
xy: Vec<i32>, // Path centerline coordinates. [x0, y0, x1, y1,...]
|
||
|
properties: HashMap::<i16, Vec<u8>>, //Properties for the element.
|
||
|
}
|
||
|
|
||
|
impl Element for Path {
|
||
|
fn parse(input: &[u8]) -> IResult<&[u8], Self> {
|
||
|
let mut path_type = 0;
|
||
|
let mut width = 0;
|
||
|
let mut bgn_ext = 0;
|
||
|
let mut end_ext = 0;
|
||
|
let (input, mut layer) = LAYER::skip_and_read(input)?;
|
||
|
let (input, mut dtype) = DATATYPE::read(input)?;
|
||
|
|
||
|
let (input, mut header) = RecordHeader::parse(&input)?;
|
||
|
while header.tag != records::RTAG_XY {
|
||
|
match header.tag {
|
||
|
records::RTAG_PATHTYPE =>
|
||
|
{let (input, path_type) = PATHTYPE::read_data(input, header.data_size)?;},
|
||
|
records::RTAG_WIDTH =>
|
||
|
{let (input, width) = WIDTH::read_data(input, header.data_size)?;},
|
||
|
records::RTAG_BGNEXTN =>
|
||
|
{let (input, bgn_ext) = BGNEXTN::read_data(input, header.data_size)?;},
|
||
|
records::RTAG_ENDEXTN =>
|
||
|
{let (input, end_ext) = ENDEXTN::read_data(input, header.data_size)?;},
|
||
|
_ =>
|
||
|
return Err(format!("Unexpected tag {:04x}", header.tag)),
|
||
|
};
|
||
|
let (input, header) = RecordHeader::parse(&input)?;
|
||
|
}
|
||
|
let (input, xy) = XY::read_data(input, header.data_size)?;
|
||
|
let (input, properties) = read_properties(input)?;
|
||
|
Path{
|
||
|
layer: (layer, dtype),
|
||
|
xy: xy,
|
||
|
properties: properties,
|
||
|
extension: (bgn_ext, end_ext),
|
||
|
path_type: path_type,
|
||
|
width: width,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fn write<W: Write>(&self, ww: W) -> OWResult {
|
||
|
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)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
struct GDSBox {
|
||
|
/*
|
||
|
* Datastructure representing a Box element. Rarely used.
|
||
|
*/
|
||
|
layer: (i16, i16), // (layer, box_type) tuple
|
||
|
xy: Vec<i32>, // Box coordinates (5 pairs)
|
||
|
properties: HashMap::<i16, Vec<u8>>, // Properties for the element.
|
||
|
}
|
||
|
|
||
|
impl Element for GDSBox {
|
||
|
fn parse(input: &[u8]) -> IResult<&[u8], Self> {
|
||
|
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)?;
|
||
|
GDSBox{
|
||
|
layer: (layer, dtype),
|
||
|
xy: xy,
|
||
|
properties: properties,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fn write<W: Write>(&self, ww: W) -> OWResult {
|
||
|
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)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
struct Node {
|
||
|
/*
|
||
|
* Datastructure representing a Node element. Rarely used.
|
||
|
*/
|
||
|
layer: (i16, i16), // (layer, box_type) tuple
|
||
|
xy: Vec<i32>, // 1-50 pairs of coordinates.
|
||
|
properties: HashMap::<i16, Vec<u8>>, // Properties for the element.
|
||
|
}
|
||
|
|
||
|
impl Element for Node {
|
||
|
fn parse(input: &[u8]) -> IResult<&[u8], Self> {
|
||
|
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)?;
|
||
|
Node{
|
||
|
layer: (layer, dtype),
|
||
|
xy: xy,
|
||
|
properties: properties,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fn write<W: Write>(&self, ww: W) -> OWResult {
|
||
|
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)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
struct Text {
|
||
|
/*
|
||
|
* Datastructure representing a text label.
|
||
|
*/
|
||
|
layer: (i16, i16), // (layer, node_type) tuple
|
||
|
presentation: [bool; 16],
|
||
|
/*
|
||
|
* 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
|
||
|
*/
|
||
|
path_type: i16, // Default 0
|
||
|
width: i32, // Default 0
|
||
|
invert_y: bool, // Vertical inversion. Default false.
|
||
|
mag: f64, // Scaling factor. Default 1.
|
||
|
angle_deg: f64, // Rotation (ccw). Default 0.
|
||
|
xy: Vec<i32>, // Position (1 pair only)
|
||
|
string: Vec<u8>, // Text content
|
||
|
properties: HashMap::<i16, Vec<u8>> // Properties for the element.
|
||
|
}
|
||
|
|
||
|
impl Element for Text {
|
||
|
fn parse(input: &[u8]) -> IResult<&[u8], Self> {
|
||
|
let mut path_type = 0;
|
||
|
let mut presentation = 0;
|
||
|
let mut invert_y = false;
|
||
|
let mut width = 0;
|
||
|
let mut mag = 1;
|
||
|
let mut angle_deg = 0;
|
||
|
let (input, layer) = LAYER::skip_and_read(input)?;
|
||
|
let (input, dtype) = TEXTTYPE::read(input)?;
|
||
|
|
||
|
let mut header = RecordHeader::parse(input)?;
|
||
|
while header.tag != records::RTAG_XY {
|
||
|
match header.tag {
|
||
|
records::RTAG_PRESENTATION =>
|
||
|
{let (input, presentation) = PRESENTATION::read_data(input, header.data_size)?;},
|
||
|
records::RTAG_PATHTYPE =>
|
||
|
{let (input, path_type) = PATHTYPE::read_data(input, header.data_size)?;},
|
||
|
records::RTAG_WIDTH =>
|
||
|
{let (input, width) = WIDTH::read_data(input, header.data_size)?;},
|
||
|
records::RTAG_STRANS => {
|
||
|
let (input, strans) = STRANS::read_data(input, header.data_size)?;
|
||
|
invert_y = strans[0];
|
||
|
},
|
||
|
records::RTAG_MAG =>
|
||
|
{let (input, mag) = MAG::read_data(input, header.data_size)?;},
|
||
|
records::RTAG_ANGLE =>
|
||
|
{let (input, angle_deg) = ANGLE::read_data(input, header.data_size)?;},
|
||
|
_ =>
|
||
|
return Err(format!("Unexpected tag {:04x}", header.tag)),
|
||
|
}
|
||
|
let (input, header) = RecordHeader::parse(input)?;
|
||
|
}
|
||
|
let (input, xy) = XY::read_data(input, header.data_size)?;
|
||
|
|
||
|
let (input, string) = STRING::read(input)?;
|
||
|
let (input, properties) = read_properties(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<W: Write>(&self, ww: W) -> OWResult {
|
||
|
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 != 0 {
|
||
|
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 || self.mag != 1 || self.invert_y {
|
||
|
let strans = [false; 16];
|
||
|
strans[0] = self.invert_y;
|
||
|
size += STRANS::write(ww, strans)?;
|
||
|
if self.mag != 1 {
|
||
|
size += MAG::write(ww, self.mag)?;
|
||
|
}
|
||
|
if self.angle_deg !=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)
|
||
|
}
|
||
|
}
|