klamath-rs/src/elements.rs

1084 lines
39 KiB
Rust

///
/// Functionality for reading/writing elements (geometry, text labels,
/// structure references) and associated properties.
///
use crate::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 crate::records;
use crate::record::{RecordHeader, Record};
use crate::basic::{IResult, fail, take_bytes}; //OResult
use std::string::String;
use std::collections::HashMap;
//use std::io::Write;
use std::sync::Arc;
use arrow::datatypes::{DataType, Field, Fields};
use arrow::array::{
StructBuilder, ListBuilder, StringBuilder, ArrayBuilder, Float64Builder, BooleanBuilder,
Int32Builder, Int16Builder, UInt64Builder, UInt32Builder, UInt8Builder,
StructArray,
};
type DListBuilder = ListBuilder<Box<dyn ArrayBuilder>>;
pub fn read_library(input: &[u8]) -> IResult<StructArray> {
let input_size = input.len();
let property_t = DataType::Struct(Fields::from(vec![
Field::new("key", DataType::Int16, false),
Field::new("value", DataType::Utf8, false),
]));
let property_list_t = DataType::List(Arc::new(
Field::new_list_field(property_t, false)
));
let repetition_struct_t = DataType::Struct(Fields::from(vec![
Field::new("x0", DataType::Int32, false),
Field::new("y0", DataType::Int32, false),
Field::new("x1", DataType::Int32, false),
Field::new("y1", DataType::Int32, false),
Field::new("count0", DataType::Int16, false),
Field::new("count1", DataType::Int16, false),
]));
let ref_struct_t = DataType::Struct(Fields::from(vec![
Field::new("target", DataType::UInt32, false),
Field::new("invert_y", DataType::Boolean, true),
Field::new("mag", DataType::Float64, true),
Field::new("angle_deg", DataType::Float64, true),
Field::new("x", DataType::Int32, false),
Field::new("y", DataType::Int32, false),
Field::new("repetition", repetition_struct_t, true),
Field::new("properties", property_list_t.clone(), true),
]));
let text_struct_t = DataType::Struct(Fields::from(vec![
Field::new("layer", DataType::UInt16, false),
Field::new("dtype", DataType::UInt16, false),
Field::new("presentation_horiz", DataType::UInt8, true),
Field::new("presentation_vert", DataType::UInt8, true),
Field::new("presentation_font", DataType::UInt8, true),
Field::new("path_type", DataType::Int16, true),
Field::new("width", DataType::Int32, true),
Field::new("invert_y", DataType::Boolean, true),
Field::new("mag", DataType::Float64, true),
Field::new("angle_deg", DataType::Float64, true),
Field::new("x", DataType::Int32, false),
Field::new("y", DataType::Int32, false),
Field::new("string", DataType::Utf8, false),
Field::new("properties", property_list_t.clone(), true),
]));
let coords_t = DataType::List(Arc::new(
Field::new_list_field(DataType::Int32, false)
));
let boundary_struct_t = DataType::Struct(Fields::from(vec![
Field::new("layer", DataType::UInt16, false),
Field::new("dtype", DataType::UInt16, false),
Field::new("xy", coords_t.clone(), false),
Field::new("properties", property_list_t.clone(), true),
]));
let path_struct_t = DataType::Struct(Fields::from(vec![
Field::new("layer", DataType::UInt16, false),
Field::new("dtype", DataType::UInt16, false),
Field::new("path_type", DataType::Int16, false),
Field::new("extension_start", DataType::Int32, true),
Field::new("extension_end", DataType::Int32, true),
Field::new("width", DataType::Int32, false),
Field::new("xy", coords_t.clone(), false),
Field::new("properties", property_list_t.clone(), true),
]));
let boxnode_struct_t = DataType::Struct(Fields::from(vec![
Field::new("layer", DataType::UInt16, false),
Field::new("dtype", DataType::UInt16, false),
Field::new("xy", coords_t.clone(), false),
Field::new("properties", property_list_t.clone(), true),
]));
let ref_list_t = DataType::List(Arc::new(
Field::new_list_field(ref_struct_t, false)
));
let text_list_t = DataType::List(Arc::new(
Field::new_list_field(text_struct_t, false)
));
let boundary_list_t = DataType::List(Arc::new(
Field::new_list_field(boundary_struct_t, false)
));
let path_list_t = DataType::List(Arc::new(
Field::new_list_field(path_struct_t, false)
));
let boxnode_list_t = DataType::List(Arc::new(
Field::new_list_field(boxnode_struct_t, false)
));
let cell_struct_t = DataType::Struct(Fields::from(vec![
Field::new("id", DataType::UInt32, false),
Field::new("file_offset", DataType::UInt64, false),
Field::new("refs", ref_list_t, false),
Field::new("boundaries", boundary_list_t, false),
Field::new("paths", path_list_t, false),
Field::new("nodes", boxnode_list_t.clone(), true),
Field::new("boxes", boxnode_list_t.clone(), true),
Field::new("texts", text_list_t, false),
]));
let mut lib_builder = StructBuilder::from_fields(vec![
Field::new("cell_names", DataType::Utf8, false),
Field::new("cells", cell_struct_t, false),
],
0,
);
let cells_builder = lib_builder.field_builder::<DListBuilder>(0).unwrap();
let mut names = HashMap::<String, u32>::new();
let (mut input, mut header) = RecordHeader::read(input)?;
while header.tag != records::RTAG_ENDLIB {
(input, _) = take_bytes(input, header.data_size)?;
if header.tag == records::RTAG_BGNSTR {
let name_bytes;
(input, name_bytes) = records::STRNAME::read(input)?;
let name = String::from_utf8(name_bytes).unwrap();
let next_id = names.len();
let id = names.entry(name).or_insert(next_id.try_into().unwrap());
let position = input_size - input.len();
let cell_builder = cells_builder.values().as_any_mut().downcast_mut::<StructBuilder>().unwrap();
let id_builder = cell_builder.field_builder::<UInt32Builder>(0).unwrap();
id_builder.append_value(*id);
let offset_builder = cell_builder.field_builder::<UInt64Builder>(1).unwrap();
offset_builder.append_value(position.try_into().unwrap());
(input, _) = read_elements(input, cell_builder, &mut names)?;
cells_builder.append(true);
}
(input, header) = RecordHeader::read(input)?;
}
let mut ids: HashMap<u32, String> = names.into_iter().map(|(kk, vv)| (vv, kk)).collect();
let names_builder = lib_builder.field_builder::<StringBuilder>(1).unwrap();
for id in 0..ids.len() {
names_builder.append_value(ids.remove(&id.try_into().unwrap()).unwrap());
}
let lib = lib_builder.finish();
Ok((input, lib))
}
pub fn read_elements<'a>(input: &'a [u8], cell_builder: &mut StructBuilder, names: &mut HashMap<String, u32>) -> IResult<'a, ()> {
let (mut input, mut header) = RecordHeader::read(input)?;
while header.tag != records::RTAG_ENDSTR {
match header.tag {
records::RTAG_BOUNDARY => {(input, _) = read_boundary(input, cell_builder)?;},
records::RTAG_PATH => {read_path(input, cell_builder)?;},
records::RTAG_NODE => {read_boxnode(input, cell_builder, header.tag)?;},
records::RTAG_BOX => {read_boxnode(input, cell_builder, header.tag)?;},
records::RTAG_TEXT => {read_text(input, cell_builder)?;},
records::RTAG_SREF => {read_ref(input, cell_builder, header.tag, names)?;},
records::RTAG_AREF => {read_ref(input, cell_builder, header.tag, names)?;},
_ => {
// don't care, skip
(input, _) = take_bytes(input, header.data_size)?;
}
}
(input, header) = RecordHeader::read(input)?;
}
Ok((input, ()))
}
pub fn read_boundary<'a>(input: &'a [u8], cell_builder: &mut StructBuilder) -> IResult<'a, ()> {
let boundaries_builder = cell_builder.field_builder::<DListBuilder>(3).unwrap();
let boundary_builder = boundaries_builder.values().as_any_mut().downcast_mut::<StructBuilder>().unwrap();
let (input, _) = records::BOUNDARY::read(input)?;
let (input, layer) = LAYER::skip_and_read(input)?;
let layer_builder = boundary_builder.field_builder::<Int16Builder>(0).unwrap();
layer_builder.append_value(layer);
let (input, dtype) = DATATYPE::read(input)?;
let dtype_builder = boundary_builder.field_builder::<Int16Builder>(1).unwrap();
dtype_builder.append_value(dtype);
let xys_builder = boundary_builder.field_builder::<DListBuilder>(2).unwrap();
let xy_builder = xys_builder.values().as_any_mut().downcast_mut::<Int32Builder>().unwrap();
let (input, xy_iter) = XY::read(input)?;
for xy in xy_iter {
xy_builder.append_value(xy);
}
xys_builder.append(true);
let props_builder = boundary_builder.field_builder::<DListBuilder>(3).unwrap();
let (input, ()) = read_properties(input, props_builder)?;
boundary_builder.append(true);
boundaries_builder.append(true);
Ok((input, ()))
}
pub fn read_path<'a>(input: &'a [u8], cell_builder: &mut StructBuilder) -> IResult<'a, ()> {
let paths_builder = cell_builder.field_builder::<DListBuilder>(4).unwrap();
let path_builder = paths_builder.values().as_any_mut().downcast_mut::<StructBuilder>().unwrap();
let (input, _) = records::PATH::read(input)?;
let (input, layer) = LAYER::skip_and_read(input)?;
let (input, dtype) = DATATYPE::read(input)?;
let layer_builder = path_builder.field_builder::<Int16Builder>(0).unwrap();
layer_builder.append_value(layer);
let dtype_builder = path_builder.field_builder::<Int16Builder>(1).unwrap();
dtype_builder.append_value(dtype);
let mut path_type = None;
let mut width = None;
let mut bgn_ext = None;
let mut end_ext = None;
let (mut input, mut header) = RecordHeader::read(&input)?;
while header.tag != records::RTAG_XY {
match header.tag {
records::RTAG_PATHTYPE => {
let _path_type;
(input, _path_type) = PATHTYPE::read_data(input, header.data_size)?;
path_type = Some(_path_type);
},
records::RTAG_WIDTH => {
let _width;
(input, _width) = WIDTH::read_data(input, header.data_size)?;
width = Some(_width);
},
records::RTAG_BGNEXTN => {
let _bgn_ext;
(input, _bgn_ext) = BGNEXTN::read_data(input, header.data_size)?;
bgn_ext = Some(_bgn_ext);
},
records::RTAG_ENDEXTN => {
let _end_ext;
(input, _end_ext) = ENDEXTN::read_data(input, header.data_size)?;
end_ext = Some(_end_ext);
},
_ =>
return fail(input, format!("Unexpected tag {:04x}", header.tag)),
};
(input, header) = RecordHeader::read(&input)?;
}
let path_type_builder = path_builder.field_builder::<Int16Builder>(2).unwrap();
path_type_builder.append_option(path_type);
let ext0_builder = path_builder.field_builder::<Int32Builder>(3).unwrap();
ext0_builder.append_option(bgn_ext);
let ext1_builder = path_builder.field_builder::<Int32Builder>(4).unwrap();
ext1_builder.append_option(end_ext);
let width_builder = path_builder.field_builder::<Int32Builder>(5).unwrap();
width_builder.append_option(width);
let xys_builder = path_builder.field_builder::<DListBuilder>(6).unwrap();
let (input, xy_iter) = XY::read(input)?;
for xy in xy_iter {
let xy_builder = xys_builder.values().as_any_mut().downcast_mut::<Int32Builder>().unwrap();
xy_builder.append_value(xy);
}
xys_builder.append(true);
let props_builder = path_builder.field_builder::<DListBuilder>(7).unwrap();
let (input, ()) = read_properties(input, props_builder)?;
path_builder.append(true);
paths_builder.append(true);
Ok((input, ()))
}
pub fn read_boxnode<'a>(input: &'a [u8], cell_builder: &mut StructBuilder, tag: u16) -> IResult<'a, ()> {
let field_num = match tag {
records::RTAG_NODE => 5,
records::RTAG_BOX => 6,
_ => return fail(input, format!("Unexpected tag {:04x}", tag)),
};
let boxnodes_builder = cell_builder.field_builder::<DListBuilder>(field_num).unwrap();
let boxnode_builder = boxnodes_builder.values().as_any_mut().downcast_mut::<StructBuilder>().unwrap();
let (input, _) = match tag {
records::RTAG_NODE => records::NODE::read(input)?,
records::RTAG_BOX => records::BOX::read(input)?,
_ => return fail(input, format!("Unexpected tag {:04x}", tag)),
};
let layer_builder = boxnode_builder.field_builder::<Int16Builder>(0).unwrap();
let (input, layer) = LAYER::skip_and_read(input)?;
layer_builder.append_value(layer);
let (input, dtype) = match tag {
records::RTAG_NODE => NODETYPE::read(input)?,
records::RTAG_BOX => BOXTYPE::read(input)?,
_ => return fail(input, format!("Unexpected tag {:04x}", tag)),
};
let dtype_builder = boxnode_builder.field_builder::<Int16Builder>(1).unwrap();
dtype_builder.append_value(dtype);
let xys_builder = boxnode_builder.field_builder::<DListBuilder>(2).unwrap();
let xy_builder = xys_builder.values().as_any_mut().downcast_mut::<Int32Builder>().unwrap();
let (input, xy_iter) = XY::read(input)?;
for xy in xy_iter {
xy_builder.append_value(xy);
}
xys_builder.append(true);
let props_builder = boxnode_builder.field_builder::<DListBuilder>(3).unwrap();
let (input, ()) = read_properties(input, props_builder)?;
boxnode_builder.append(true);
boxnodes_builder.append(true);
Ok((input, ()))
}
pub fn read_text<'a>(input: &'a [u8], cell_builder: &mut StructBuilder) -> IResult<'a, ()> {
let texts_builder = cell_builder.field_builder::<DListBuilder>(7).unwrap();
let text_builder = texts_builder.values().as_any_mut().downcast_mut::<StructBuilder>().unwrap();
let mut path_type = None;
let mut pres_hori = None;
let mut pres_vert = None;
let mut pres_font = None;
let mut invert_y = None;
let mut width = None;
let mut mag = None;
let mut angle_deg = None;
let (input, layer) = LAYER::skip_and_read(input)?;
let layer_builder = text_builder.field_builder::<Int16Builder>(0).unwrap();
layer_builder.append_value(layer);
let (input, dtype) = TEXTTYPE::read(input)?;
let dtype_builder = text_builder.field_builder::<Int16Builder>(1).unwrap();
dtype_builder.append_value(dtype);
let (mut input, mut header) = RecordHeader::read(input)?;
while header.tag != records::RTAG_XY {
match header.tag {
// TODO warn if repeat tags?
records::RTAG_PRESENTATION => {
let _presentation;
(input, _presentation) = PRESENTATION::read_data(input, header.data_size)?;
pres_hori = Some(_presentation[14] as u8 * 2 + _presentation[15] as u8);
pres_vert = Some(_presentation[12] as u8 * 2 + _presentation[13] as u8);
pres_font = Some(_presentation[10] as u8 * 2 + _presentation[11] as u8);
},
records::RTAG_PATHTYPE => {
let _path_type;
(input, _path_type) = PATHTYPE::read_data(input, header.data_size)?;
path_type = Some(_path_type);
},
records::RTAG_WIDTH => {
let _width;
(input, _width) = WIDTH::read_data(input, header.data_size)?;
width = Some(_width);
},
records::RTAG_STRANS => {
let strans;
(input, strans) = STRANS::read_data(input, header.data_size)?;
invert_y = Some(strans[0]);
},
records::RTAG_MAG => {
let _mag;
(input, _mag) = MAG::read_data(input, header.data_size)?;
mag = Some(_mag);
},
records::RTAG_ANGLE => {
let _angle_deg;
(input, _angle_deg) = ANGLE::read_data(input, header.data_size)?;
angle_deg = Some(_angle_deg);
},
_ =>
return fail(input, format!("Unexpected tag {:04x}", header.tag)),
}
(input, header) = RecordHeader::read(input)?;
}
let pres_hori_builder = text_builder.field_builder::<UInt8Builder>(2).unwrap();
pres_hori_builder.append_option(pres_hori);
let pres_vert_builder = text_builder.field_builder::<UInt8Builder>(3).unwrap();
pres_vert_builder.append_option(pres_vert);
let pres_font_builder = text_builder.field_builder::<UInt8Builder>(4).unwrap();
pres_font_builder.append_option(pres_font);
let path_type_builder = text_builder.field_builder::<Int16Builder>(5).unwrap();
path_type_builder.append_option(path_type);
let width_builder = text_builder.field_builder::<Int32Builder>(6).unwrap();
width_builder.append_option(width);
let inv_builder = text_builder.field_builder::<BooleanBuilder>(7).unwrap();
inv_builder.append_option(invert_y);
let mag_builder = text_builder.field_builder::<Float64Builder>(8).unwrap();
mag_builder.append_option(mag);
let angle_builder = text_builder.field_builder::<Float64Builder>(9).unwrap();
angle_builder.append_option(angle_deg);
let (input, mut xy_iter) = XY::read(input)?;
let x_builder = text_builder.field_builder::<Int32Builder>(10).unwrap();
x_builder.append_value(xy_iter.next().unwrap());
let y_builder = text_builder.field_builder::<Int32Builder>(11).unwrap();
y_builder.append_value(xy_iter.next().unwrap());
let (input, string_bytes) = STRING::read(input)?;
let string = String::from_utf8(string_bytes).unwrap();
let string_builder = text_builder.field_builder::<StringBuilder>(12).unwrap();
string_builder.append_value(string);
let props_builder = text_builder.field_builder::<DListBuilder>(13).unwrap();
let (input, ()) = read_properties(input, props_builder)?;
text_builder.append(true);
texts_builder.append(true);
Ok((input, ()))
}
pub fn read_ref<'a>(input: &'a [u8], cell_builder: &mut StructBuilder, tag: u16, names: &mut HashMap<String, u32>) -> IResult<'a, ()> {
let is_aref = tag == records::RTAG_AREF;
let refs_builder = cell_builder.field_builder::<DListBuilder>(7).unwrap();
let ref_builder = refs_builder.values().as_any_mut().downcast_mut::<StructBuilder>().unwrap();
let mut invert_y = None;
let mut mag = None;
let mut angle_deg = None;
let mut colrow = None;
let (input, struct_name_bytes) = SNAME::skip_and_read(input)?;
let struct_name = String::from_utf8(struct_name_bytes).unwrap();
let next_id = names.len();
let id = names.entry(struct_name).or_insert(next_id.try_into().unwrap());
let target_builder = ref_builder.field_builder::<UInt32Builder>(0).unwrap();
target_builder.append_value(*id);
let (mut input, mut header) = RecordHeader::read(input)?;
while header.tag != records::RTAG_XY {
match header.tag {
records::RTAG_STRANS => {
let strans;
(input, strans) = STRANS::read_data(input, header.data_size)?;
invert_y = Some(strans[0]);
},
records::RTAG_MAG => {
let _mag;
(input, _mag) = MAG::read_data(input, header.data_size)?;
mag = Some(_mag);
},
records::RTAG_ANGLE => {
let _angle_deg;
(input, _angle_deg) = ANGLE::read_data(input, header.data_size)?;
angle_deg = Some(_angle_deg);
},
records::RTAG_COLROW => {
let mut _colrow;
(input, _colrow) = COLROW::read_data(input, header.data_size)?;
colrow = Some((_colrow.next().unwrap(), _colrow.next().unwrap()));
if !is_aref {
return fail(input, "Got a COLROW record inside an SREF".to_string());
}
},
_ =>
return fail(input, format!("Unexpected tag {:04x}", header.tag)),
};
(input, header) = RecordHeader::read(input)?;
}
let inv_builder = ref_builder.field_builder::<BooleanBuilder>(1).unwrap();
inv_builder.append_option(invert_y);
let mag_builder = ref_builder.field_builder::<Float64Builder>(2).unwrap();
mag_builder.append_option(mag);
let angle_builder = ref_builder.field_builder::<Float64Builder>(3).unwrap();
angle_builder.append_option(angle_deg);
let (input, mut xy_iter) = XY::read_data(input, header.data_size)?;
let x_builder = ref_builder.field_builder::<Int32Builder>(4).unwrap();
x_builder.append_value(xy_iter.next().unwrap());
let y_builder = ref_builder.field_builder::<Int32Builder>(5).unwrap();
y_builder.append_value(xy_iter.next().unwrap());
let rep_builder = ref_builder.field_builder::<StructBuilder>(6).unwrap();
if is_aref {
let x0_builder = rep_builder.field_builder::<Int32Builder>(0).unwrap();
x0_builder.append_value(xy_iter.next().unwrap());
let y0_builder = rep_builder.field_builder::<Int32Builder>(1).unwrap();
y0_builder.append_value(xy_iter.next().unwrap());
let x1_builder = rep_builder.field_builder::<Int32Builder>(2).unwrap();
x1_builder.append_value(xy_iter.next().unwrap());
let y1_builder = rep_builder.field_builder::<Int32Builder>(3).unwrap();
y1_builder.append_value(xy_iter.next().unwrap());
match colrow {
None => return fail(input, "AREF without COLROW before XY".to_string()),
Some((count0, count1)) => {
let count0_builder = rep_builder.field_builder::<Int16Builder>(4).unwrap();
count0_builder.append_value(count0);
let count1_builder = rep_builder.field_builder::<Int16Builder>(5).unwrap();
count1_builder.append_value(count1);
},
}
}
rep_builder.append(is_aref);
let props_builder = ref_builder.field_builder::<DListBuilder>(7).unwrap();
let (input, ()) = read_properties(input, props_builder)?;
ref_builder.append(true);
refs_builder.append(true);
Ok((input, ()))
}
///
/// 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<'a>(input: &'a [u8], props_builder: &mut DListBuilder) -> IResult<'a, ()> {
let prop_builder = props_builder.values().as_any_mut().downcast_mut::<StructBuilder>().unwrap();
let (mut input, mut header) = RecordHeader::read(input)?;
while header.tag != ENDEL::tag() {
if header.tag == PROPATTR::tag() {
let key;
let value_bytes;
(input, key) = PROPATTR::read_data(input, header.data_size)?;
(input, value_bytes) = PROPVALUE::read(input)?;
let value = String::from_utf8(value_bytes).unwrap();
//assert!(!properties.contains_key(&key), "Duplicate property key: {}", key);
let key_builder = prop_builder.field_builder::<Int16Builder>(0).unwrap();
key_builder.append_value(key);
let val_builder = prop_builder.field_builder::<StringBuilder>(1).unwrap();
val_builder.append_value(value);
prop_builder.append(true);
}
(input, header) = RecordHeader::read(input)?;
}
props_builder.append(true);
Ok((input, ()))
}
/*
///
/// Write element properties.
///
/// This is does _not_ write the ENDEL record.
///
/// Args:
/// stream: Stream to write to.
///
pub fn write_properties<W: Write>(ww: &mut W, properties: &HashMap::<i16, Vec<u8>>) -> 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<Self> where Self: Sized;
///
/// Write this element to a stream.
/// Finishes with an ENDEL record.
///
fn write<W: 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<u8>,
/// 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<i32>,
/// Number of columns and rows (AREF) || None (SREF)
colrow: Option<(i16, i16)>,
/// Properties associated with this reference.
properties: HashMap::<i16, Vec<u8>>,
}
impl Element for Reference {
fn read(input: &[u8]) -> IResult<Self> {
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<W: 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<i32>,
/// Properties for the element.
properties: HashMap::<i16, Vec<u8>>,
}
impl Element for Boundary {
fn read(input: &[u8]) -> IResult<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)?;
Ok((input, Boundary{
layer: (layer, dtype),
xy: xy,
properties: properties,
}))
}
fn write<W: 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<i32>,
/// Properties for the element.
properties: HashMap::<i16, Vec<u8>>,
}
impl Element for Path {
fn read(input: &[u8]) -> IResult<Self> {
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<W: 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<i32>,
/// Properties for the element.
properties: HashMap::<i16, Vec<u8>>,
}
impl Element for GDSBox {
fn read(input: &[u8]) -> IResult<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)?;
Ok((input, GDSBox{
layer: (layer, dtype),
xy: xy,
properties: properties,
}))
}
fn write<W: 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<i32>,
/// Properties for the element.
properties: HashMap::<i16, Vec<u8>>,
}
impl Element for Node {
fn read(input: &[u8]) -> IResult<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)?;
Ok((input, Node{
layer: (layer, dtype),
xy: xy,
properties: properties,
}))
}
fn write<W: 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<i32>,
/// Text content
string: Vec<u8>,
/// Properties for the element.
properties: HashMap::<i16, Vec<u8>>
}
impl Element for Text {
fn read(input: &[u8]) -> IResult<Self> {
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<W: 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)
}
}
*/