klamath-rs/src/elements.rs

482 lines
16 KiB
Rust
Raw Normal View History

2021-12-18 21:05:00 -08:00
/*
* 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)
}
}