use crate::Modal;
use libreda_db::layout::prelude::*;
use crate::base_types::*;
use std::io::Write;
use std::ptr;
#[derive(Copy, Clone, Debug)]
pub enum OASISValidationScheme {
None,
}
impl Default for OASISValidationScheme {
fn default() -> Self {
OASISValidationScheme::None
}
}
#[derive(Clone, Debug, Default)]
pub struct OASISWriterConfig {
table_offsets_at_start: bool,
explicit_ids: bool,
validation_scheme: OASISValidationScheme,
}
#[derive(Copy, Clone, Debug, Default)]
struct Offset {
flag: UInt,
offset: UInt,
}
#[derive(Clone, Debug, Default)]
struct OffsetTable {
cellname: Offset,
textstring: Offset,
propname: Offset,
propstring: Offset,
layername: Offset,
xname: Offset,
}
fn write_offset_table<W: Write>(writer: &mut W, t: &OffsetTable) -> Result<(), OASISWriteError> {
for o in &[t.cellname, t.textstring, t.propname, t.propstring, t.layername, t.xname] {
write_unsigned_integer(writer, o.flag)?;
write_unsigned_integer(writer, o.offset)?;
}
Ok(())
}
fn bit(index: UInt, value: bool) -> UInt {
(value as UInt) << index
}
pub fn write_layout<W: Write>(writer: &mut W, layout: &Layout, conf: &OASISWriterConfig) -> Result<(), OASISWriteError> {
let mut modal = Modal::default();
write_magic(writer)?;
write_unsigned_integer(writer, 1)?;
write_ascii_string(writer, "1.0".as_bytes())?;
if layout.dbu == 0 {
return Err(OASISWriteError::DbuIsZero);
}
let resolution = Real::PositiveWholeNumber(layout.dbu);
write_real(writer, resolution)?;
let offset_flag = if conf.table_offsets_at_start { 0 } else { 1 };
write_unsigned_integer(writer, offset_flag)?;
if conf.table_offsets_at_start {
write_offset_table(writer, &OffsetTable::default())?;
}
let mut _cellname_id_counter = (0..).into_iter();
let mut _textstrings_id_counter = (0..).into_iter();
let mut _propname_id_counter = (0..).into_iter();
let mut _propstrings_id_counter = (0..).into_iter();
for cell in layout.each_cell() {
debug!("write cell '{:?}'", cell.name());
write_unsigned_integer(writer, 14)?;
write_name_string(writer, cell.name()
.expect("Cell has no name!")
.as_bytes())?;
modal.reset();
for l in cell.each_used_layer() {
debug!("write shapes on layer '{:?}'", l);
let layer_info = layout.get_layer_info(l).unwrap();
let layer_index = layer_info.index;
let layer_datatype = layer_info.datatype;
for shape in cell.each_shape(l) {
match &shape.geometry {
Geometry::Rect(r) => {
write_unsigned_integer(writer, 20)?;
let width = r.width() as UInt;
let height = r.height() as UInt;
let (x, y) = r.lower_left.into();
let is_square = width == height;
let write_layer_number = modal.layer != Some(layer_index);
let write_datatype = modal.datatype != Some(layer_datatype);
let write_width = modal.geometry_w != Some(width);
let write_height = !is_square && modal.geometry_h != Some(height);
let write_x = modal.geometry_x != x;
let write_y = modal.geometry_y != y;
let write_repetition = false;
let rectangle_info_byte =
bit(7, is_square)
| bit(6, write_width)
| bit(5, write_height)
| bit(4, write_x)
| bit(3, write_y)
| bit(2, write_repetition)
| bit(1, write_datatype)
| bit(0, write_layer_number);
write_byte(writer, rectangle_info_byte as u8)?;
if write_layer_number { write_unsigned_integer(writer, layer_index)? };
if write_datatype { write_unsigned_integer(writer, layer_datatype)? };
if write_width { write_unsigned_integer(writer, width)? };
if write_height { write_unsigned_integer(writer, height)? };
if write_x { write_signed_integer(writer, x)? };
if write_y { write_signed_integer(writer, y)? };
if write_repetition { unimplemented!(); };
modal.geometry_w = Some(width);
modal.geometry_h = Some(height);
modal.geometry_x = x;
modal.geometry_y = y;
modal.datatype = Some(layer_datatype);
modal.layer = Some(layer_index);
}
Geometry::SimplePolygon(p) => {
write_unsigned_integer(writer, 21)?;
if p.len() > 1 {
let offset = p.points.first().unwrap();
let point_list = PointList::from_points_explicit(&p.points);
let (x, y) = offset.into();
let write_layer_number = modal.layer != Some(layer_index);
let write_datatype = modal.layer != Some(layer_datatype);
let write_x = modal.geometry_x != x;
let write_y = modal.geometry_y != y;
let write_repetition = false;
let is_pointlist_present = Some(&point_list) != modal.polygon_point_list.as_ref();
let polygon_info_byte =
bit(5, is_pointlist_present)
| bit(4, write_x)
| bit(3, write_y)
| bit(2, write_repetition)
| bit(1, write_datatype)
| bit(0, write_layer_number);
write_byte(writer, polygon_info_byte as u8)?;
if write_layer_number { write_unsigned_integer(writer, layer_index)? };
if write_datatype { write_unsigned_integer(writer, layer_datatype)? };
if is_pointlist_present { write_pointlist(writer, &point_list)? };
if write_x { write_signed_integer(writer, x)? };
if write_y { write_signed_integer(writer, y)? };
if write_repetition {
modal.repetition = None;
unimplemented!();
};
modal.polygon_point_list = Some(point_list);
modal.geometry_x = x;
modal.geometry_y = y;
modal.datatype = Some(layer_datatype);
modal.layer = Some(layer_index);
} else {
}
}
Geometry::Path(p) => {
write_unsigned_integer(writer, 22)?;
if p.len() > 0 {
let offset = p.points.points.first().unwrap();
let point_list = PointList::from_points_explicit(&p.points.points);
let (x, y) = offset.into();
let half_width = (p.width as UInt) / 2;
let (start_ext, end_ext) = match p.path_type {
PathEndType::Flat => (0, 0),
PathEndType::Extended(s, e) => (s, e),
PathEndType::Round => unimplemented!("Round path endings are not supported yet.")
};
let write_layer_number = modal.layer != Some(layer_index);
let write_datatype = modal.layer != Some(layer_datatype);
let write_x = modal.geometry_x != x;
let write_y = modal.geometry_y != y;
let write_repetition = false;
let write_point_list = Some(&point_list) != modal.path_point_list.as_ref();
let write_half_width_present = modal.path_halfwidth != Some(half_width);
let write_extension_scheme_present = modal.path_start_extension != Some(start_ext)
|| modal.path_end_extension != Some(end_ext);
let path_info_byte =
bit(7, write_extension_scheme_present)
| bit(6, write_half_width_present)
| bit(5, write_point_list)
| bit(4, write_x)
| bit(3, write_y)
| bit(2, write_repetition)
| bit(1, write_datatype)
| bit(0, write_layer_number);
write_byte(writer, path_info_byte as u8)?;
if write_layer_number { write_unsigned_integer(writer, layer_index)? };
if write_datatype { write_unsigned_integer(writer, layer_datatype)? };
if write_half_width_present { write_unsigned_integer(writer, half_width)? };
if write_extension_scheme_present {
let ss = match start_ext {
0 => 0b01,
x if x == half_width as SInt => 0b10,
x if Some(x) == modal.path_start_extension => 0b00,
_ => 0b11
};
let ee = match end_ext {
0 => 0b01,
x if x == half_width as SInt => 0b10,
x if Some(x) == modal.path_end_extension => 0b00,
_ => 0b11
};
let extension_scheme = (ss << 2) | ee;
write_byte(writer, extension_scheme)?;
if ss == 0b11 {
write_signed_integer(writer, start_ext)?;
}
if ee == 0b11 {
write_signed_integer(writer, end_ext)?;
}
};
if write_point_list { write_pointlist(writer, &point_list)? };
if write_x { write_signed_integer(writer, x)? };
if write_y { write_signed_integer(writer, y)? };
if write_repetition {
modal.repetition = None;
unimplemented!();
};
modal.path_point_list = Some(point_list);
modal.path_halfwidth = Some(half_width);
modal.path_start_extension = Some(start_ext);
modal.path_end_extension = Some(end_ext);
modal.geometry_x = x;
modal.geometry_y = y;
modal.datatype = Some(layer_datatype);
modal.layer = Some(layer_index);
} else {
}
}
Geometry::Text(text) => {
debug!("TEXT record");
write_unsigned_integer(writer, 19)?;
let text_string = text.text();
let x = text.x();
let y = text.y();
let write_text_reference = modal.text_string.as_ref() != Some(text_string);
let write_reference_number = false;
let write_textlayer = modal.textlayer != Some(layer_index);
let write_texttype = modal.texttype != Some(layer_datatype);
let write_x = modal.text_x != x;
let write_y = modal.text_y != y;
let write_repetition = false;
let text_info_byte =
bit(6, write_text_reference)
| bit(5, write_reference_number)
| bit(4, write_x)
| bit(3, write_y)
| bit(2, write_repetition)
| bit(1, write_texttype)
| bit(0, write_textlayer);
write_byte(writer, text_info_byte as u8)?;
if write_text_reference {
if write_reference_number {
unimplemented!()
} else {
write_ascii_string(writer, text_string.as_bytes())?
}
}
if write_textlayer { write_unsigned_integer(writer, layer_index)? };
if write_texttype { write_unsigned_integer(writer, layer_datatype)? };
if write_x { write_signed_integer(writer, x)? };
if write_y { write_signed_integer(writer, y)? };
if write_repetition {
modal.repetition = None;
unimplemented!();
};
modal.text_x = x;
modal.text_y = y;
modal.texttype = Some(layer_datatype);
modal.textlayer = Some(layer_index);
if write_text_reference {
modal.text_string = Some(text_string.clone());
}
}
_ => unimplemented!()
}
}
}
for inst in cell.each_inst() {
let placement_cell = inst.cell().upgrade().unwrap();
let tf = inst.get_transform();
if tf.magnification == 1 {
write_unsigned_integer(writer, 17)?;
let (x, y) = tf.displacement.into();
let aa = match tf.rotation {
Angle::R0 => 0,
Angle::R90 => 1,
Angle::R180 => 2,
Angle::R270 => 3,
};
let is_explicit_reference =
modal.placement_cell.as_ref()
.map(|c| !ptr::eq(c.as_ref(), placement_cell.as_ref()))
.unwrap_or(true);
let is_cell_ref_present = false;
let is_flip = tf.mirror;
let write_x = modal.placement_x != x;
let write_y = modal.placement_y != y;
let write_repetition = false;
let placement_info_byte = bit(0, is_flip)
| (aa << 1)
| bit(3, write_repetition)
| bit(4, write_y)
| bit(5, write_x)
| bit(6, is_cell_ref_present)
| bit(7, is_explicit_reference);
write_byte(writer, placement_info_byte as u8)?;
if is_explicit_reference {
if is_cell_ref_present {
unimplemented!();
} else {
write_name_string(writer, placement_cell.name()
.expect("Cell has no name!").as_bytes())?;
}
}
if write_x { write_signed_integer(writer, x)? };
if write_y { write_signed_integer(writer, y)? };
if write_repetition {
modal.repetition = None;
unimplemented!();
};
modal.placement_cell = Some(placement_cell);
modal.placement_x = x;
modal.placement_y = y;
} else {
unimplemented!("Magnifications other than 1 are not supported yet.");
}
}
}
let mut end_record_buf = Vec::new();
write_unsigned_integer(&mut end_record_buf, 2)?;
if !conf.table_offsets_at_start {
write_offset_table(&mut end_record_buf, &OffsetTable::default())?;
}
let padding_length = 256 - end_record_buf.len() - 1 - 1 - 2;
writer.write(end_record_buf.as_slice())?;
write_byte_string(writer, vec![0u8; padding_length].as_slice())?;
let validation_scheme = match conf.validation_scheme {
OASISValidationScheme::None => 0,
};
write_unsigned_integer(writer, validation_scheme)?;
match conf.validation_scheme {
OASISValidationScheme::None => {
write_unsigned_integer(writer, 0)?;
}
};
writer.flush()?;
Ok(())
}