use crate::Modal;
use libreda_db::layout::prelude::*;
use crate::base_types::*;
use std::io::Write;
use libreda_db::layout::traits::LayoutBase;
use std::borrow::Borrow;
#[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,
pub explicit_ids: bool,
pub 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
}
fn write_path<W: Write, C>(
writer: &mut W,
modal: &mut Modal<C>,
p: &Path<Coord>,
layer_index: UInt,
layer_datatype: UInt) -> Result<(), OASISWriteError>
{
debug!("Write PATH record");
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 {
}
Ok(())
}
fn write_rect<W: Write, C>(
writer: &mut W,
modal: &mut Modal<C>,
r: &Rect<Coord>,
layer_index: UInt,
layer_datatype: UInt) -> Result<(), OASISWriteError> {
debug!("Write RECT record");
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);
Ok(())
}
fn write_simple_polygon<W: Write, C>(
writer: &mut W,
modal: &mut Modal<C>,
p: &SimplePolygon<Coord>,
layer_index: UInt,
layer_datatype: UInt) -> Result<(), OASISWriteError> {
debug!("Write POLYGON record");
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 {
}
Ok(())
}
fn write_text<W: Write, C>(
writer: &mut W,
modal: &mut Modal<C>,
text: &Text<Coord>,
layer_index: UInt,
layer_datatype: UInt) -> Result<(), OASISWriteError> {
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());
}
Ok(())
}
pub fn write_layout<W: Write, L: LayoutBase<Coord=SInt>>(writer: &mut W, layout: &L, conf: &OASISWriterConfig)
-> Result<(), OASISWriteError> {
debug!("Write layout to OASIS");
let mut modal = Modal::default();
write_magic(writer)?;
write_unsigned_integer(writer, 1)?;
write_ascii_string(writer, "1.0".as_bytes())?;
if layout.dbu().is_zero() {
return Err(OASISWriteError::DbuIsZero);
}
let resolution = Real::PositiveWholeNumber(layout.dbu() as u32);
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() {
let cell_name_raw = layout.cell_name(&cell);
let cell_name: &str = cell_name_raw.borrow();
debug!("write cell '{:?}'", cell_name);
write_unsigned_integer(writer, 14)?;
write_name_string(writer, cell_name.as_bytes())?;
modal.reset();
for layer_id in layout.each_layer() {
debug!("write shapes on layer '{:?}'", layer_id);
let layer_info = layout.layer_info(&layer_id);
let layer_index = layer_info.index;
let layer_datatype = layer_info.datatype;
let mut shapes = Vec::new();
layout.for_each_shape(&cell, &layer_id, |_id, geo| shapes.push(geo.clone()));
for geometry in &shapes {
match geometry {
Geometry::Rect(r) =>
write_rect(writer, &mut modal, r, layer_index, layer_datatype)?,
Geometry::SimplePolygon(p) =>
write_simple_polygon(writer, &mut modal, p, layer_index, layer_datatype)?,
Geometry::SimpleRPolygon(p) =>
write_simple_polygon(writer, &mut modal, &p.to_simple_polygon(), layer_index, layer_datatype)?,
Geometry::Polygon(p) => {
if !p.interiors.is_empty() {
unimplemented!("Polygons with holes are not supported yet.");
}
write_simple_polygon(writer, &mut modal, &p.exterior, layer_index, layer_datatype)?;
}
Geometry::Path(p) =>
write_path(writer, &mut modal, &p, layer_index, layer_datatype)?,
Geometry::Text(text) =>
write_text(writer, &mut modal, &text, layer_index, layer_datatype)?,
Geometry::Edge(_) => {
}
Geometry::Point(_) => {
}
}
}
}
for inst in layout.each_cell_instance(&cell) {
let placement_cell = layout.template_cell(&inst);
let tf = layout.get_transform(&inst);
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() != Some(&placement_cell);
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 {
let n = layout.cell_name(&placement_cell);
let name: &str = n.borrow();
write_name_string(writer, 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(())
}