Compare commits

..

No commits in common. "master" and "release" have entirely different histories.

31 changed files with 2135 additions and 8490 deletions

30
.flake8
View File

@ -1,30 +0,0 @@
[flake8]
ignore =
# E501 line too long
E501,
# W391 newlines at EOF
W391,
# E241 multiple spaces after comma
E241,
# E302 expected 2 newlines
E302,
# W503 line break before binary operator (to be deprecated)
W503,
# E265 block comment should start with '# '
E265,
# E123 closing bracket does not match indentation of opening bracket's line
E123,
# E124 closing bracket does not match visual indentation
E124,
# E221 multiple spaces before operator
E221,
# E201 whitespace after '['
E201,
# E741 ambiguous variable name 'I'
E741,
per-file-ignores =
# F401 import without use
*/__init__.py: F401,
__init__.py: F401,

7
.gitignore vendored
View File

@ -1,14 +1,7 @@
*.pyc
__pycache__
*.idea
.mypy_cache/
build
dist
fatamorgana.egg-info
docs
*.gds
*.gds.gz
*.oas
*.oas.gz

2
MANIFEST.in Normal file
View File

@ -0,0 +1,2 @@
include README.md
include LICENSE.md

View File

@ -3,8 +3,6 @@
**fatamorgana** is a Python package for reading and writing OASIS format layout files.
**Homepage:** https://mpxd.net/code/jan/fatamorgana
* [PyPI](https://pypi.org/project/fatamorgana)
* [Github mirror](https://github.com/anewusername/fatamorgana)
**Capabilities:**
* This package is a work-in-progress and is largely untested -- it works for
@ -22,18 +20,18 @@
## Installation
**Dependencies:**
* python >=3.11
* python 3.5 or newer
* (optional) numpy
Install with pip from PyPi (preferred):
```bash
pip3 install fatamorgana
pip install fatamorgana
```
Install directly from git repository:
```bash
pip3 install git+https://mpxd.net/code/jan/fatamorgana.git@release
pip install git+https://mpxd.net/code/jan/fatamorgana.git@release
```
## Documentation

View File

@ -1 +0,0 @@
../LICENSE.md

View File

@ -1 +0,0 @@
../README.md

View File

@ -15,37 +15,14 @@
numpy to speed up reading/writing.
Dependencies:
- Python 3.11 or later
- numpy (optional, faster but no additional functionality)
To get started, try:
```python3
import fatamorgana
help(fatamorgana.OasisLayout)
```
- Python 3.5 or later
- numpy (optional, no additional functionality)
"""
from .main import (
OasisLayout as OasisLayout,
Cell as Cell,
XName as XName,
)
from .basic import (
NString as NString,
AString as AString,
Validation as Validation,
OffsetTable as OffsetTable,
OffsetEntry as OffsetEntry,
EOFError as EOFError,
SignedError as SignedError,
InvalidDataError as InvalidDataError,
InvalidRecordError as InvalidRecordError,
UnfilledModalError as UnfilledModalError,
ReuseRepetition as ReuseRepetition,
GridRepetition as GridRepetition,
ArbitraryRepetition as ArbitraryRepetition,
)
from .main import OasisLayout, Cell, XName
from .basic import NString, AString, Validation, OffsetTable, OffsetEntry, \
EOFError, SignedError, InvalidDataError, InvalidRecordError
__author__ = 'Jan Petykiewicz'
__version__ = '0.13'
version = __version__
version = '0.4'

File diff suppressed because it is too large Load Diff

View File

@ -3,22 +3,18 @@ This module contains data structures and functions for reading from and
writing to whole OASIS layout files, and provides a few additional
abstractions for the data contained inside them.
"""
from typing import IO
import io
import logging
from . import records
from .records import Modals, Record
from .basic import (
OffsetEntry, OffsetTable, NString, AString, real_t, Validation,
read_magic_bytes, write_magic_bytes, read_uint, EOFError,
InvalidRecordError,
)
from .records import Modals
from .basic import OffsetEntry, OffsetTable, NString, AString, real_t, Validation, \
read_magic_bytes, write_magic_bytes, read_uint, EOFError, \
InvalidDataError, InvalidRecordError
__author__ = 'Jan Petykiewicz'
#logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
@ -27,21 +23,17 @@ class FileModals:
"""
File-scoped modal variables
"""
cellname_implicit: bool | None = None
propname_implicit: bool | None = None
xname_implicit: bool | None = None
textstring_implicit: bool | None = None
propstring_implicit: bool | None = None
cellname_implicit = None # type: bool or None
propname_implicit = None # type: bool or None
xname_implicit = None # type: bool or None
textstring_implicit = None # type: bool or None
propstring_implicit = None # type: bool or None
cellname_implicit = None # type: bool or None
property_target: list[records.Property]
within_cell: bool = False
within_cblock: bool = False
end_has_offset_table: bool = False
started: bool = False
def __init__(self, property_target: list[records.Property]) -> None:
self.property_target = property_target
within_cell = False # type: bool
within_cblock = False # type: bool
end_has_offset_table = None # type: bool
started = False # type: bool
class OasisLayout:
@ -51,56 +43,49 @@ class OasisLayout:
Names and strings are stored in dicts, indexed by reference number.
Layer names and properties are stored directly using their associated
record objects.
Cells are stored using `Cell` objects (different from `records.Cell`
record objects).
Cells are stored using Cell objects (different from Cell record objects).
Properties:
File properties:
.version AString: Version string ('1.0')
.unit real number: grid steps per micron
.validation Validation: checksum data
Names:
.cellnames Dict[int, NString]
.propnames Dict[int, NString]
.xnames Dict[int, XName]
Strings:
.textstrings Dict[int, AString]
.propstrings Dict[int, AString]
Data:
.layers List[records.LayerName]
.properties List[records.Property]
.cells List[Cell]
"""
# File properties
version: AString
"""File format version string ('1.0')"""
version = None # type: AString
unit = None # type: real_t
validation = None # type: Validation
unit: real_t
"""grid steps per micron"""
properties = None # type: List[records.Property]
cells = None # type: List[Cell]
validation: Validation
"""checksum data"""
cellnames = None # type: Dict[int, NString]
propnames = None # type: Dict[int, NString]
xnames = None # type: Dict[int, XName]
# Data
properties: list[records.Property]
"""Property values"""
textstrings = None # type: Dict[int, AString]
propstrings = None # type: Dict[int, AString]
layers = None # type: List[records.LayerName]
cells: list['Cell']
"""Layout cells"""
layers: list[records.LayerName]
"""Layer definitions"""
# Names
cellnames: dict[int, 'CellName']
"""Cell names"""
propnames: dict[int, NString]
"""Property names"""
xnames: dict[int, 'XName']
"""Custom names"""
# String storage
textstrings: dict[int, AString]
"""Text strings"""
propstrings: dict[int, AString]
"""Property strings"""
def __init__(
self,
unit: real_t,
validation: Validation | None = None,
) -> None:
def __init__(self, unit: real_t, validation: Validation = None):
"""
Args:
unit: Real number (i.e. int, float, or `Fraction`), grid steps per micron.
validation: `Validation` object containing checksum data.
Default creates a `Validation` object of the "no checksum" type.
:param unit: Real number (i.e. int, float, or Fraction), grid steps per micron.
:param validation: Validation object containing checksum data.
Default creates a Validation object of the "no checksum" type.
"""
if validation is None:
validation = Validation(0)
@ -118,19 +103,16 @@ class OasisLayout:
self.layers = []
@staticmethod
def read(stream: IO[bytes]) -> 'OasisLayout':
def read(stream: io.BufferedIOBase) -> 'OasisLayout':
"""
Read an entire .oas file into an `OasisLayout` object.
Read an entire .oas file into an OasisLayout object.
Args:
stream: Stream to read from.
Returns:
New `OasisLayout` object.
:param stream: Stream to read from.
:return: New OasisLayout object.
"""
layout = OasisLayout(unit=-1) # dummy unit
file_state = FileModals()
modals = Modals()
file_state = FileModals(layout.properties)
layout = OasisLayout(unit=None)
read_magic_bytes(stream)
@ -138,39 +120,32 @@ class OasisLayout:
pass
return layout
def read_record(
self,
stream: IO[bytes],
modals: Modals,
file_state: FileModals
) -> bool:
def read_record(self,
stream: io.BufferedIOBase,
modals: Modals,
file_state: FileModals
) -> bool:
"""
Read a single record of unspecified type from a stream, adding its
contents into this `OasisLayout` object.
contents into this OasisLayout object.
Args:
stream: Stream to read from.
modals: Modal variable data, used to fill unfilled record
fields and updated using filled record fields.
file_state: File status data.
Returns:
`True` if EOF was reached without error, `False` otherwise.
Raises:
InvalidRecordError: from unexpected records
InvalidDataError: from within record parsers
:param stream: Stream to read from.
:param modals: Modal variable data, used to fill unfilled record
fields and updated using filled record fields.
:param file_state: File status data.
:return: True if EOF was reached without error, False otherwise.
:raises: InvalidRecordError from unexpected records;
InvalidDataError from within record parsers.
"""
try:
record_id = read_uint(stream)
except EOFError:
except EOFError as e:
if file_state.within_cblock:
return True
raise
else:
raise e
logger.info(f'read_record of type {record_id} at position 0x{stream.tell():x}')
record: Record
logger.info('read_record of type {} at position 0x{:x}'.format(record_id, stream.tell()))
# CBlock
if record_id == 34:
@ -188,11 +163,12 @@ class OasisLayout:
# Make sure order is valid (eg, no out-of-cell geometry)
if not file_state.started and record_id != 1:
raise InvalidRecordError(f'Non-Start record {record_id} before Start')
raise InvalidRecordError('Non-Start record {} before Start'.format(record_id))
if record_id == 1:
if file_state.started:
raise InvalidRecordError('Duplicate Start record')
file_state.started = True
else:
file_state.started = True
if record_id == 2 and file_state.within_cblock:
raise InvalidRecordError('End within CBlock')
@ -200,28 +176,25 @@ class OasisLayout:
pass
elif record_id in range(3, 13) or record_id in (28, 29):
file_state.within_cell = False
elif record_id in range(15, 28) or record_id in (32, 33):
elif record_id in range(15, 29) or record_id in (32, 33):
if not file_state.within_cell:
raise InvalidRecordError('Geometry outside Cell')
raise Exception('Geometry outside Cell')
elif record_id in (13, 14):
file_state.within_cell = True
else:
raise InvalidRecordError(f'Unknown record id: {record_id}')
raise InvalidRecordError('Unknown record id: {}'.format(record_id))
if record_id == 0:
''' Pad '''
# Pad
pass
elif record_id == 1:
''' Start '''
record = records.Start.read(stream, record_id)
record.merge_with_modals(modals)
self.unit = record.unit
self.version = record.version
file_state.end_has_offset_table = record.offset_table is None
file_state.property_target = self.properties
# TODO Offset table strict check
elif record_id == 2:
''' End '''
record = records.End.read(stream, record_id, file_state.end_has_offset_table)
record.merge_with_modals(modals)
self.validation = record.validation
@ -229,7 +202,6 @@ class OasisLayout:
raise InvalidRecordError('Stream continues past End record')
return True
elif record_id in (3, 4):
''' CellName '''
implicit = record_id == 3
if file_state.cellname_implicit is None:
file_state.cellname_implicit = implicit
@ -241,12 +213,8 @@ class OasisLayout:
key = record.reference_number
if key is None:
key = len(self.cellnames)
cellname = CellName.from_record(record)
self.cellnames[key] = cellname
file_state.property_target = cellname.properties
self.cellnames[key] = record.nstring
elif record_id in (5, 6):
''' TextString '''
implicit = record_id == 5
if file_state.textstring_implicit is None:
file_state.textstring_implicit = implicit
@ -260,7 +228,6 @@ class OasisLayout:
key = len(self.textstrings)
self.textstrings[key] = record.astring
elif record_id in (7, 8):
''' PropName '''
implicit = record_id == 7
if file_state.propname_implicit is None:
file_state.propname_implicit = implicit
@ -274,7 +241,6 @@ class OasisLayout:
key = len(self.propnames)
self.propnames[key] = record.nstring
elif record_id in (9, 10):
''' PropString '''
implicit = record_id == 9
if file_state.propstring_implicit is None:
file_state.propstring_implicit = implicit
@ -288,17 +254,17 @@ class OasisLayout:
key = len(self.propstrings)
self.propstrings[key] = record.astring
elif record_id in (11, 12):
''' LayerName '''
record = records.LayerName.read(stream, record_id)
record.merge_with_modals(modals)
self.layers.append(record)
elif record_id in (28, 29):
''' Property '''
record = records.Property.read(stream, record_id)
record.merge_with_modals(modals)
file_state.property_target.append(record)
if not file_state.within_cell:
self.properties.append(record)
else:
self.cells[-1].properties.append(record)
elif record_id in (30, 31):
''' XName '''
implicit = record_id == 30
if file_state.xname_implicit is None:
file_state.xname_implicit = implicit
@ -311,69 +277,53 @@ class OasisLayout:
if key is None:
key = len(self.xnames)
self.xnames[key] = XName.from_record(record)
# TODO: do anything with property target?
#
# Cell and elements
#
elif record_id in (13, 14):
''' Cell '''
record = records.Cell.read(stream, record_id)
record.merge_with_modals(modals)
cell = Cell(record.name)
self.cells.append(cell)
file_state.property_target = cell.properties
self.cells.append(Cell(record.name))
elif record_id in (15, 16):
''' XYMode '''
record = records.XYMode.read(stream, record_id)
record.merge_with_modals(modals)
elif record_id in (17, 18):
''' Placement '''
record = records.Placement.read(stream, record_id)
record.merge_with_modals(modals)
self.cells[-1].placements.append(record)
file_state.property_target = record.properties
elif record_id in _GEOMETRY:
''' Geometry '''
record = _GEOMETRY[record_id].read(stream, record_id)
record.merge_with_modals(modals)
self.cells[-1].geometry.append(record)
file_state.property_target = record.properties
else:
raise InvalidRecordError(f'Unknown record id: {record_id}')
raise InvalidRecordError('Unknown record id: {}'.format(record_id))
return False
def write(self, stream: IO[bytes]) -> int:
def write(self, stream: io.BufferedIOBase) -> int:
"""
Write this object in OASIS fromat to a stream.
Args:
stream: Stream to write to.
Returns:
Number of bytes written.
Raises:
InvalidDataError: if contained records are invalid.
:param stream: Stream to write to.
:return: Number of bytes written.
:raises: InvalidDataError if contained records are invalid.
"""
modals = Modals()
size = 0
size += write_magic_bytes(stream)
size += records.Start(self.unit, self.version).dedup_write(stream, modals)
size += sum(p.dedup_write(stream, modals) for p in self.properties)
cellnames_offset = OffsetEntry(False, size)
for refnum, cn in self.cellnames.items():
size += records.CellName(cn.nstring, refnum).dedup_write(stream, modals)
size += sum(p.dedup_write(stream, modals) for p in cn.properties)
size += sum(records.CellName(name, refnum).dedup_write(stream, modals)
for refnum, name in self.cellnames.items())
propnames_offset = OffsetEntry(False, size)
size += sum(records.PropName(name, refnum).dedup_write(stream, modals)
for refnum, name in self.propnames.items())
xnames_offset = OffsetEntry(False, size)
size += sum(records.XName(x.attribute, x.bstring, refnum).dedup_write(stream, modals)
size += sum(records.XName(x.attribute, x.string, refnum).dedup_write(stream, modals)
for refnum, x in self.xnames.items())
textstrings_offset = OffsetEntry(False, size)
@ -387,6 +337,8 @@ class OasisLayout:
layernames_offset = OffsetEntry(False, size)
size += sum(r.dedup_write(stream, modals) for r in self.layers)
size += sum(p.dedup_write(stream, modals) for p in self.properties)
size += sum(c.dedup_write(stream, modals) for c in self.cells)
offset_table = OffsetTable(
@ -404,109 +356,59 @@ class OasisLayout:
class Cell:
"""
Representation of an OASIS cell.
Properties:
.name NString or int (CellName reference number)
.properties List of records.Property
.placements List of records.Placement
.geometry List of geometry record objectes
"""
name: NString | int
"""name or "CellName reference" number"""
name = None # type: NString or int
properties = None # type: List[records.Property]
placements = None # type: List[records.Placement]
geometry = None # type: List[records.geometry_t]
properties: list[records.Property]
placements: list[records.Placement]
geometry: list[records.geometry_t]
def __init__(self, name: NString or int):
"""
:param name: NString or int (CellName reference number)
"""
self.name = name
self.properties = []
self.placements = []
self.geometry = []
def __init__(
self,
name: NString | str | int,
*,
properties: list[records.Property] | None = None,
placements: list[records.Placement] | None = None,
geometry: list[records.geometry_t] | None = None,
) -> None:
self.name = name if isinstance(name, NString | int) else NString(name)
self.properties = [] if properties is None else properties
self.placements = [] if placements is None else placements
self.geometry = [] if geometry is None else geometry
def dedup_write(self, stream: IO[bytes], modals: Modals) -> int:
def dedup_write(self, stream: io.BufferedIOBase, modals: Modals) -> int:
"""
Write this cell to a stream, using the provided modal variables to
deduplicate any repeated data.
Args:
stream: Stream to write to.
modals: Modal variables to use for deduplication.
Returns:
Number of bytes written.
Raises:
InvalidDataError: if contained records are invalid.
:param stream: Stream to write to.
:param modals: Modal variables to use for deduplication.
:return: Number of bytes written.
:raises: InvalidDataError if contained records are invalid.
"""
size = records.Cell(self.name).dedup_write(stream, modals)
size += sum(p.dedup_write(stream, modals) for p in self.properties)
for placement in self.placements:
size += placement.dedup_write(stream, modals)
size += sum(p.dedup_write(stream, modals) for p in placement.properties)
for shape in self.geometry:
size += shape.dedup_write(stream, modals)
size += sum(p.dedup_write(stream, modals) for p in shape.properties)
size += sum(p.dedup_write(stream, modals) for p in self.placements)
size += sum(g.dedup_write(stream, modals) for g in self.geometry)
return size
class CellName:
"""
Representation of a CellName.
This class is effectively a simplified form of a `records.CellName`,
with the reference data stripped out.
"""
nstring: NString
properties: list[records.Property]
def __init__(
self,
nstring: NString | str,
properties: list[records.Property] | None = None,
) -> None:
"""
Args:
nstring: The contained string.
properties: Properties which apply to this CellName's cell, but
are placed following the CellName record.
"""
if isinstance(nstring, NString):
self.nstring = nstring
else:
self.nstring = NString(nstring)
self.properties = [] if properties is None else properties
@staticmethod
def from_record(record: records.CellName) -> 'CellName':
"""
Create an `CellName` object from a `records.CellName` record.
Args:
record: CellName record to use.
Returns:
A new `CellName` object.
"""
return CellName(record.nstring)
class XName:
"""
Representation of an XName.
This class is effectively a simplified form of a `records.XName`,
This class is effectively a simplified form of a records.XName,
with the reference data stripped out.
"""
attribute: int
bstring: bytes
attribute = None # type: int
bstring = None # type: bytes
def __init__(self, attribute: int, bstring: bytes) -> None:
def __init__(self, attribute: int, bstring: bytes):
"""
Args:
attribute: Attribute number.
bstring: Binary data.
:param attribute: Attribute number.
:param bstring: Binary data.
"""
self.attribute = attribute
self.bstring = bstring
@ -514,19 +416,16 @@ class XName:
@staticmethod
def from_record(record: records.XName) -> 'XName':
"""
Create an `XName` object from a `records.XName` record.
Create an XName object from a records.XName record.
Args:
record: XName record to use.
Returns:
a new `XName` object.
:param record: XName record to use.
:return: XName object.
"""
return XName(record.attribute, record.bstring)
# Mapping from record id to record class.
_GEOMETRY: dict[int, type[records.geometry_t]] = {
_GEOMETRY = {
19: records.Text,
20: records.Rectangle,
21: records.Polygon,

View File

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +0,0 @@
"""
Tests (run with `python3 -m pytest -rxPXs | tee results.txt`)
The test_files_* modules are meant to mimic the test cases used by KLayout,
in order to provide a secondary validation mechanism.
"""

View File

@ -1,97 +0,0 @@
"""
Build files equivalent to the test cases used by KLayout.
"""
from typing import IO
from collections.abc import Callable
from pathlib import Path
from . import (
test_files_properties, test_files_cblocks, test_files_layernames,
test_files_circles, test_files_ctrapezoids, test_files_trapezoids,
test_files_placements, test_files_paths, test_files_modals,
test_files_polygons, test_files_rectangles, test_files_empty,
test_files_texts, test_files_cells,
)
def build_file(num: str, func: Callable[[IO[bytes]], IO[bytes]]) -> None:
with Path('t' + num + '.oas').open('wb') as ff:
func(ff)
def write_all_files() -> None:
build_file('1.1', test_files_empty.write_file_1)
build_file('1.2', test_files_empty.write_file_2)
build_file('1.3', test_files_empty.write_file_3)
build_file('1.4', test_files_empty.write_file_4)
build_file('1.5', test_files_empty.write_file_5)
build_file('2.1', test_files_cells.write_file_1)
build_file('2.2', test_files_cells.write_file_2)
build_file('2.3', test_files_cells.write_file_3)
build_file('2.4', test_files_cells.write_file_4)
build_file('2.5', test_files_cells.write_file_5)
build_file('2.6', test_files_cells.write_file_6)
build_file('2.7', test_files_cells.write_file_7)
build_file('3.1', lambda f: test_files_texts.write_file_common(f, 1))
build_file('3.2', lambda f: test_files_texts.write_file_common(f, 2))
build_file('3.3', test_files_texts.write_file_3)
build_file('3.4', test_files_texts.write_file_4)
build_file('3.5', lambda f: test_files_texts.write_file_common(f, 5))
build_file('3.6', test_files_texts.write_file_6)
build_file('3.7', test_files_texts.write_file_7)
build_file('3.8', test_files_texts.write_file_8)
build_file('3.9', test_files_texts.write_file_9)
build_file('3.10', test_files_texts.write_file_10)
build_file('3.11', test_files_texts.write_file_11)
build_file('4.1', lambda f: test_files_rectangles.write_file_common(f, 1))
build_file('4.2', lambda f: test_files_rectangles.write_file_common(f, 2))
build_file('5.1', lambda f: test_files_polygons.write_file_common(f, 1))
build_file('5.2', test_files_polygons.write_file_2)
build_file('5.3', lambda f: test_files_polygons.write_file_common(f, 3))
build_file('6.1', test_files_paths.write_file_1)
build_file('7.1', test_files_trapezoids.write_file_1)
build_file('8.1', test_files_placements.write_file_1)
build_file('8.2', lambda f: test_files_placements.write_file_common(f, 2))
build_file('8.3', lambda f: test_files_placements.write_file_common(f, 3))
build_file('8.4', test_files_placements.write_file_4)
build_file('8.5', lambda f: test_files_placements.write_file_common(f, 5))
build_file('8.6', test_files_placements.write_file_6)
build_file('8.7', lambda f: test_files_placements.write_file_common(f, 7))
build_file('8.8', test_files_placements.write_file_8)
build_file('9.1', test_files_ctrapezoids.write_file_1)
build_file('9.2', test_files_ctrapezoids.write_file_2)
build_file('10.1', test_files_modals.write_file_1)
build_file('11.1', lambda f: test_files_properties.write_file_common(f, 1))
build_file('11.2', lambda f: test_files_properties.write_file_common(f, 2))
build_file('11.3', test_files_properties.write_file_3)
build_file('11.4', lambda f: test_files_properties.write_file_4_6(f, 4))
build_file('11.5', lambda f: test_files_properties.write_file_common(f, 5))
build_file('11.6', lambda f: test_files_properties.write_file_4_6(f, 6))
build_file('11.7', lambda f: test_files_properties.write_file_7_8_9(f, 7))
build_file('11.8', lambda f: test_files_properties.write_file_7_8_9(f, 8))
build_file('11.9', lambda f: test_files_properties.write_file_7_8_9(f, 9))
build_file('12.1', test_files_circles.write_file_1)
build_file('13.1', test_files_layernames.write_file_1)
build_file('13.2', test_files_layernames.write_file_2)
build_file('13.3', test_files_layernames.write_file_3)
build_file('13.4', test_files_layernames.write_file_4)
build_file('14.1', test_files_cblocks.write_file_1)
if __name__ == '__main__':
write_all_files()

View File

@ -1,196 +0,0 @@
# mypy: disable-error-code="union-attr"
from typing import IO
from io import BytesIO
from numpy.testing import assert_equal
from .utils import HEADER, FOOTER
from ..basic import write_uint, write_bstring, write_byte
from ..main import OasisLayout
def base_tests(layout: OasisLayout) -> None:
assert layout.version.string == '1.0'
assert layout.unit == 1000
assert layout.validation.checksum_type == 0
assert not layout.properties
assert not layout.propnames
assert not layout.xnames
assert not layout.textstrings
assert not layout.cellnames
assert not layout.layers
assert len(layout.cells) == 1
assert layout.cells[0].name.string == 'ABCDH'
assert not layout.cells[0].properties
def write_file_1(buf: IO[bytes]) -> IO[bytes]:
"""
"""
buf.write(HEADER)
write_uint(buf, 14) # CELL record (explicit)
write_bstring(buf, b'ABCDH') # Cell name
cblock_data = bytes.fromhex('''
22 00 b0 02 b2 02 13
a9 66 60 98 c3 32 89 e5 0e e3 1b 61 91 4a c6 15
ac 8f 58 3a f8 be f0 8a 5a b0 30 57 5f 64 6d e4
4f bd c8 7a 87 ed 81 f8 02 79 a0 88 68 f5 42 b6
4e be 80 99 4c 3b 99 35 97 30 4f 14 d7 3c 14 f4
52 50 e4 24 e3 0b f6 9b c2 1a 9a 27 18 57 4b 8a
04 ae 65 3f 12 04 24 36 0b 8b 2c f2 e9 14 16 3d
c6 73 92 4d 64 21 e3 0b 9e cf 9a 15 4f 59 6f 08
83 cc 5d c8 f8 91 7b 25 7f ea 4e e6 03 3c 5b a4
66 88 01 85 d8 37 b2 fc 64 bd c8 25 5a 79 92 b1
99 4b a3 93 65 26 7b 33 bf e6 69 b6 39 7c a9 4b
40 2e e1 3b 28 a6 79 82 69 41 98 f6 14 ae 60 9b
d7 4c a2 9a 3d 8c 37 f9 6c 03 3f 32 b6 68 2c 64
5c cb f3 9a 49 f3 33 e3 0c a6 dd da 29 2f 98 76
80 d4 73 df 64 f9 cb b3 58 33 60 36 d3 13 d6 9b
9c b6 9a 3b 98 5f b2 07 2e 64 dc c9 7c 91 4b 24
f8 08 cb 6e 45 8d 47 32 1d 12 77 b8 81 4a 59 17
68 6a 1f 60 df 28 ac a9 3d 85 b5 5b b6 62 0a ff
0c 69 90 7b 36 b3 6c 65 d3 9c c9 f4 40 b1 93 a5
47 e0 32 7f 8a e6 54 d6 93 6c a2 0f 14 17 c8 03
00''')
for byte in cblock_data:
write_byte(buf, byte)
buf.write(FOOTER)
return buf
def test_file_1() -> None:
buf = write_file_1(BytesIO())
buf.seek(0)
layout = OasisLayout.read(buf)
base_tests(layout)
geometry = layout.cells[0].geometry
assert len(geometry) == 10
for ii, gg in enumerate(geometry):
msg = f'Failed on geometry {ii}'
assert gg.x == [110, 900, 1520, -370, 1690, -50, 180, 1540, 970, 2160][ii], msg
assert gg.y == [1270, 890, 2000, 1260, 1420, 850, 860, 750, 1740, 2000][ii], msg
if ii == 0:
assert gg.layer == 0, msg
else:
assert gg.layer == 1, msg
assert gg.datatype == 0, msg
assert not gg.properties, msg
assert gg.repetition is None, msg
assert geometry[0].height == 530
assert geometry[0].width == 540
assert geometry[1].height == 610
assert geometry[1].width == 680
assert_equal(geometry[2].point_list, [
[-30, -360],
[480, -50],
[180, 430],
[-630, -20],
])
assert_equal(geometry[3].point_list, [
[-30, -400],
[450, 40],
[70, -220],
[10, 210],
[740, -20],
[0, 660],
[570, 10],
[50, 500],
[630, 20],
[10, 100],
[-810, 10],
[20, -470],
[-660, 0],
[20, -470],
[-620, 10],
[0, 610],
[610, -10],
[0, -100],
[210, 10],
[40, 820],
[-1340, 60],
[30, -1370],
])
assert_equal(geometry[4].point_list, [
[40, -760],
[490, -50],
[110, 800],
[-640, 10],
])
assert_equal(geometry[5].point_list, [
[140, -380],
[340, -10],
[30, -100],
[-320, 20],
[130, -460],
[-480, -20],
[-210, 910],
[370, 40],
])
assert_equal(geometry[6].point_list, [
[720, -20],
[20, 20],
[690, 0],
[-10, 650],
[-20, 30],
[-90, -10],
[10, 70],
[470, -30],
[20, -120],
[-320, 0],
[40, -790],
[-90, -20],
[-60, 140],
[-1390, 50],
[10, 30],
])
assert_equal(geometry[7].point_list, [
[150, -830],
[-1320, 40],
[-70, 370],
[310, -30],
[10, 220],
[250, -40],
[40, -220],
[340, 10],
[-20, 290],
[-1070, 20],
[0, 230],
[1380, -60],
])
assert_equal(geometry[8].point_list, [
[330, 0],
[-10, 480],
[620, -20],
[-10, 330],
[-930, 60],
[0, -850],
])
assert_equal(geometry[9].point_list, [
[-140, -410],
[10, -140],
[270, 0],
[130, 1030],
[-500, 50],
[10, -330],
[210, -10],
[10, -190],
])

View File

@ -1,272 +0,0 @@
# mypy: disable-error-code="union-attr"
from typing import IO
from io import BytesIO
import pytest
from .utils import HEADER, FOOTER
from ..basic import write_uint, write_bstring
from ..basic import InvalidRecordError, InvalidDataError
from ..main import OasisLayout
def base_tests(layout: OasisLayout) -> None:
assert layout.version.string == '1.0'
assert layout.unit == 1000
assert layout.validation.checksum_type == 0
assert not layout.properties
assert not layout.propnames
assert not layout.xnames
assert not layout.textstrings
assert not layout.propstrings
assert not layout.layers
def write_file_1(buf: IO[bytes]) -> IO[bytes]:
"""
Single cell with explicit name 'XYZ'
"""
buf.write(HEADER)
write_uint(buf, 14) # CELL record (explicit)
write_bstring(buf, b'XYZ') # Cell name
buf.write(FOOTER)
return buf
def test_file_1() -> None:
buf = write_file_1(BytesIO())
buf.seek(0)
layout = OasisLayout.read(buf)
base_tests(layout)
assert len(layout.cells) == 1
assert layout.cells[0].name.string == 'XYZ'
assert not layout.cellnames
def write_file_2(buf: IO[bytes]) -> IO[bytes]:
"""
Two cellnames ('XYZ', 'ABC') and two cells with name references.
"""
buf.write(HEADER)
write_uint(buf, 3) # CELLNAME record (implicit id 0)
write_bstring(buf, b'XYZ')
write_uint(buf, 3) # CELLNAME record (implicit id 1)
write_bstring(buf, b'ABC')
write_uint(buf, 13) # CELL record (name ref.)
write_uint(buf, 0) # Cell name 0 (XYZ)
write_uint(buf, 13) # CELL record (name ref.)
write_uint(buf, 1) # Cell name 1 (ABC)
buf.write(FOOTER)
return buf
def test_file_2() -> None:
buf = write_file_2(BytesIO())
buf.seek(0)
layout = OasisLayout.read(buf)
base_tests(layout)
assert len(layout.cellnames) == 2
assert len(layout.cells) == 2
assert layout.cellnames[0].nstring.string == 'XYZ'
assert layout.cellnames[1].nstring.string == 'ABC'
assert layout.cells[0].name == 0
assert layout.cells[1].name == 1
def write_file_3(buf: IO[bytes]) -> IO[bytes]:
"""
Invalid file, contains a mix of explicit and implicit cellnames
"""
buf.write(HEADER)
write_uint(buf, 4) # CELLNAME record (explicit id)
write_bstring(buf, b'ABC')
write_uint(buf, 1) # id 1
write_uint(buf, 3) # CELLNAME record (implicit id 0) -- Expect failure due to mix of explicit/implicit ids
write_bstring(buf, b'XYZ')
write_uint(buf, 13) # CELL record (name ref.)
write_uint(buf, 0) # Cell name 0 (XYZ)
write_uint(buf, 13) # CELL record (name ref.)
write_uint(buf, 1) # Cell name 1 (ABC)
buf.write(FOOTER)
return buf
def test_file_3() -> None:
buf = write_file_3(BytesIO())
buf.seek(0)
with pytest.raises(InvalidRecordError):
_layout = OasisLayout.read(buf)
def write_file_4(buf: IO[bytes]) -> IO[bytes]:
"""
Two cells referencing two names with explicit ids (unsorted)
"""
buf.write(HEADER)
write_uint(buf, 4) # CELLNAME record (explicit id)
write_bstring(buf, b'ABC')
write_uint(buf, 1) # id 1
write_uint(buf, 4) # CELLNAME record (explicit id)
write_bstring(buf, b'XYZ')
write_uint(buf, 0) # id 0
write_uint(buf, 13) # CELL record (name ref.)
write_uint(buf, 0) # Cell name 0 (XYZ)
write_uint(buf, 13) # CELL record (name ref.)
write_uint(buf, 1) # Cell name 1 (ABC)
buf.write(FOOTER)
return buf
def test_file_4() -> None:
buf = write_file_4(BytesIO())
buf.seek(0)
layout = OasisLayout.read(buf)
base_tests(layout)
assert len(layout.cellnames) == 2
assert len(layout.cells) == 2
assert layout.cellnames[0].nstring.string == 'XYZ'
assert layout.cellnames[1].nstring.string == 'ABC'
assert layout.cells[0].name == 0
assert layout.cells[1].name == 1
def write_file_5(buf: IO[bytes]) -> IO[bytes]:
"""
Reference to non-existent cell name.
"""
buf.write(HEADER)
write_uint(buf, 4) # CELLNAME record (explicit id)
write_bstring(buf, b'ABC')
write_uint(buf, 1) # id 1
write_uint(buf, 4) # CELLNAME record (explicit id)
write_bstring(buf, b'XYZ')
write_uint(buf, 0) # id 0
write_uint(buf, 13) # CELL record (name ref.)
write_uint(buf, 0) # Cell name 0 (XYZ)
write_uint(buf, 13) # CELL record (name ref.)
write_uint(buf, 2) # Cell name 2 -- Reference to non-existent CELLNAME!!!
buf.write(FOOTER)
return buf
def test_file_5() -> None:
buf = write_file_5(BytesIO())
buf.seek(0)
layout = OasisLayout.read(buf)
base_tests(layout)
assert len(layout.cellnames) == 2
assert len(layout.cells) == 2
assert layout.cellnames[0].nstring.string == 'XYZ'
assert layout.cellnames[1].nstring.string == 'ABC'
assert layout.cells[0].name == 0
assert layout.cells[1].name == 2
#TODO add optional error checking for this case
def write_file_6(buf: IO[bytes]) -> IO[bytes]:
"""
Cellname with invalid n-string.
"""
buf.write(HEADER)
write_uint(buf, 4) # CELLNAME record (explicit id)
write_bstring(buf, b'ABC')
write_uint(buf, 1) # id 1
write_uint(buf, 4) # CELLNAME record (explicit id)
write_bstring(buf, b' XYZ')
write_uint(buf, 0) # id 0
write_uint(buf, 13) # CELL record (name ref.)
write_uint(buf, 0) # Cell name 0 (XYZ)
write_uint(buf, 13) # CELL record (name ref.)
write_uint(buf, 1) # Cell name 1
buf.write(FOOTER)
return buf
def test_file_6() -> None:
buf = write_file_6(BytesIO())
buf.seek(0)
with pytest.raises(InvalidDataError):
_layout = OasisLayout.read(buf)
#base_tests(layout)
#assert len(layout.cellnames) == 2
#assert len(layout.cells) == 2
#assert layout.cellnames[0].nstring.string == ' XYZ'
#assert layout.cellnames[1].nstring.string == 'ABC'
#assert layout.cells[0].name == 0
#assert layout.cells[1].name == 1
def write_file_7(buf: IO[bytes]) -> IO[bytes]:
"""
Unused cellname.
"""
buf.write(HEADER)
write_uint(buf, 4) # CELLNAME record (explicit id)
write_bstring(buf, b'ABC')
write_uint(buf, 1) # id 1
write_uint(buf, 4) # CELLNAME record (explicit id)
write_bstring(buf, b'XYZ')
write_uint(buf, 0) # id 0
write_uint(buf, 13) # CELL record (name ref.)
write_uint(buf, 0) # Cell name 0 (XYZ)
buf.write(FOOTER)
return buf
def test_file_7() -> None:
buf = write_file_7(BytesIO())
buf.seek(0)
layout = OasisLayout.read(buf)
base_tests(layout)
assert len(layout.cellnames) == 2
assert len(layout.cells) == 1
assert layout.cellnames[0].nstring.string == 'XYZ'
assert layout.cellnames[1].nstring.string == 'ABC'
assert layout.cells[0].name == 0

View File

@ -1,109 +0,0 @@
# mypy: disable-error-code="union-attr"
from typing import IO
from io import BytesIO
from .utils import HEADER, FOOTER
from ..basic import write_uint, write_sint, write_bstring, write_byte
from ..main import OasisLayout
def base_tests(layout: OasisLayout) -> None:
assert layout.version.string == '1.0'
assert layout.unit == 1000
assert layout.validation.checksum_type == 0
assert not layout.properties
assert not layout.propnames
assert not layout.xnames
assert not layout.textstrings
assert not layout.cellnames
assert not layout.layers
assert len(layout.cells) == 1
assert layout.cells[0].name.string == 'A'
assert not layout.cells[0].properties
def write_file_1(buf: IO[bytes]) -> IO[bytes]:
"""
"""
buf.write(HEADER)
write_uint(buf, 14) # CELL record (explicit)
write_bstring(buf, b'A') # Cell name
write_uint(buf, 27) # CIRCLE record
write_byte(buf, 0b0011_1011) # 00rX_YRDL
write_uint(buf, 1) # layer
write_uint(buf, 2) # datatype
write_uint(buf, 150) # radius
write_sint(buf, -100) # geometry-x (absolute)
write_sint(buf, 200) # geometry-y (absolute)
write_uint(buf, 16) # XYRELATIVE record
write_uint(buf, 27) # CIRCLE record
write_byte(buf, 0b0000_1000) # 00rX_YRDL
write_sint(buf, 400) # geometry-y (relative)
write_uint(buf, 27) # CIRCLE record
write_byte(buf, 0b0010_1000) # 00rX_YRDL
write_uint(buf, 0) # radius
write_sint(buf, 400) # geometry-y (relative)
write_uint(buf, 27) # CIRCLE record
write_byte(buf, 0b0010_1000) # 00rX_YRDL
write_uint(buf, 1) # radius
write_sint(buf, 400) # geometry-y (relative)
write_uint(buf, 27) # CIRCLE record
write_byte(buf, 0b0010_1000) # 00rX_YRDL
write_uint(buf, 6) # radius
write_sint(buf, 400) # geometry-y (relative)
write_uint(buf, 27) # CIRCLE record
write_byte(buf, 0b0010_1000) # 00rX_YRDL
write_uint(buf, 20) # radius
write_sint(buf, 400) # geometry-y (relative)
write_uint(buf, 27) # CIRCLE record
write_byte(buf, 0b0010_1100) # 00rX_YRDL
write_uint(buf, 100) # radius
write_sint(buf, 400) # geometry-y (relative)
write_uint(buf, 1) # repetition (3x4 matrix)
write_uint(buf, 1) # (repetition) x-dimension
write_uint(buf, 2) # (repetition) y-dimension
write_uint(buf, 400) # (repetition) x-spacing
write_uint(buf, 300) # (repetition) y-spacing
buf.write(FOOTER)
return buf
def test_file_1() -> None:
buf = write_file_1(BytesIO())
buf.seek(0)
layout = OasisLayout.read(buf)
base_tests(layout)
geometry = layout.cells[0].geometry
assert len(geometry) == 7
for ii, gg in enumerate(geometry):
msg = f'Failed on circle {ii}'
assert gg.x == -100, msg
assert gg.y == 200 + 400 * ii, msg
assert gg.layer == 1, msg
assert gg.datatype == 2, msg
assert not gg.properties, msg
assert gg.radius == [150, 150, 0, 1, 6, 20, 100][ii], msg
if ii != 6:
assert gg.repetition is None, msg
assert geometry[6].repetition.a_count == 3, msg
assert geometry[6].repetition.b_count == 4, msg
assert geometry[6].repetition.a_vector == [400, 0], msg
assert geometry[6].repetition.b_vector == [0, 300], msg

View File

@ -1,239 +0,0 @@
# mypy: disable-error-code="union-attr"
from typing import IO
from io import BytesIO
from .utils import HEADER, FOOTER
from ..basic import write_uint, write_sint, write_bstring, write_byte
from ..main import OasisLayout
def base_tests(layout: OasisLayout) -> None:
assert layout.version.string == '1.0'
assert layout.unit == 1000
assert layout.validation.checksum_type == 0
assert not layout.properties
assert not layout.propnames
assert not layout.xnames
assert not layout.textstrings
assert not layout.cellnames
assert not layout.layers
def write_file_1(buf: IO[bytes]) -> IO[bytes]:
"""
"""
buf.write(HEADER)
write_uint(buf, 14) # CELL record (explicit)
write_bstring(buf, b'A') # Cell name
write_uint(buf, 26) # CTRAPEZOID record
write_byte(buf, 0b1111_1011) # TWHX_YRDL
write_uint(buf, 1) # layer
write_uint(buf, 2) # datatype
write_uint(buf, 24) # ctrapezoid type
write_uint(buf, 100) # width
write_uint(buf, 200) # height
write_sint(buf, -100) # geometry-x (absolute)
write_sint(buf, 200) # geometry-y (absolute)
write_uint(buf, 16) # XYRELATIVE record
write_uint(buf, 26) # CTRAPEZOID record
write_byte(buf, 0b0000_1000) # TWHX_YRDL
write_sint(buf, 400) # geometry-y (relative)
write_uint(buf, 20) # RECTANGLE record
write_byte(buf, 0b0000_0011) # SWHX_YRDL
write_uint(buf, 2) # layer
write_uint(buf, 3) # datatype
h = [250, 100]
v = [100, 250]
wh = [h] * 8 + [v] * 8 + [h] * 6 + [v] * 2 + [h] * 2
wh_en = ([0b11] * 16
+ [0b10] * 4
+ [0b01] * 2
+ [0b10] * 2
+ [0b11, 0b10]
)
for t, (x, x_en) in enumerate(zip(wh, wh_en, strict=True)):
write_uint(buf, 26) # CTRAPEZOID record
write_byte(buf, 0b1000_1011 | (x_en << 5)) # TWHX_YRDL
write_uint(buf, 1) # layer
write_uint(buf, 2) # datatype
write_uint(buf, t) # ctrapezoid type
if x_en & 0b10:
write_uint(buf, x[0]) # width
if x_en & 0b01:
write_uint(buf, x[1]) # height
write_sint(buf, 400) # geometry-y (relative)
write_uint(buf, 20) # RECTANGLE record
write_byte(buf, 0b0000_0011) # SWHX_YRDL
write_uint(buf, 2) # layer
write_uint(buf, 3) # datatype
write_uint(buf, 26) # CTRAPEZOID record
write_byte(buf, 0b0000_1100) # TWHX_YRDL
write_sint(buf, 400) # geometry-y (relative)
write_uint(buf, 1) # repetition (3x4 matrix)
write_uint(buf, 1) # (repetition) x-dimension
write_uint(buf, 2) # (repetition) y-dimension
write_uint(buf, 400) # (repetition) x-spacing
write_uint(buf, 300) # (repetition) y-spacing
buf.write(FOOTER)
return buf
def test_file_1() -> None:
buf = write_file_1(BytesIO())
buf.seek(0)
layout = OasisLayout.read(buf)
base_tests(layout)
assert len(layout.cells) == 1
assert layout.cells[0].name.string == 'A'
assert not layout.cells[0].properties
geometry = layout.cells[0].geometry
assert len(geometry) == 3 + 26 * 2 + 1
for ii, gg in enumerate(geometry):
msg = f'Failed on shape {ii}'
assert gg.x == -100, msg
assert gg.y == 200 + 400 * ((ii + 1) // 2), msg
if ii < 2 or (3 <= ii < 55 and ii % 2 == 1):
assert gg.layer == 1, msg
assert gg.datatype == 2, msg
else:
assert gg.layer == 2, msg
assert gg.datatype == 3, msg
if ii < 3:
assert gg.width == 100, msg
assert gg.height == 200, msg
assert not gg.properties, msg
if 3 <= ii < 55:
ct_type = (ii - 3) // 2
is_ctrapz = ii % 2 == 1
if is_ctrapz:
assert gg.ctrapezoid_type == ct_type, msg
if ct_type in range(16, 20):
assert gg.height == [250, None][is_ctrapz], msg
elif ct_type in (20, 21):
assert gg.width == [250, None][is_ctrapz], msg
elif ct_type in range(22, 24) or ct_type == 25:
assert gg.height == [100, None][is_ctrapz], msg
elif ct_type < 8 or 16 <= ct_type < 25 or ct_type >= 26:
assert gg.width == 250, msg
assert gg.height == 100, msg
else:
assert gg.width == 100, msg
assert gg.height == 250, msg
elif ii < 3 and ii % 2:
assert gg.ctrapezoid_type == 24, msg
elif ii == 55:
assert gg.ctrapezoid_type == 25, msg
assert geometry[55].repetition.a_count == 3
assert geometry[55].repetition.b_count == 4
assert geometry[55].repetition.a_vector == [400, 0]
assert geometry[55].repetition.b_vector == [0, 300]
def write_file_2(buf: IO[bytes]) -> IO[bytes]:
"""
"""
buf.write(HEADER)
write_uint(buf, 14) # CELL record (explicit)
write_bstring(buf, b'A') # Cell name
# Shouldn't access (undefined) height modal, despite not having a height.
write_uint(buf, 26) # CTRAPEZOID record
write_byte(buf, 0b1101_1011) # TWHX_YRDL
write_uint(buf, 1) # layer
write_uint(buf, 2) # datatype
write_uint(buf, 16) # ctrapezoid type
write_uint(buf, 200) # width
write_sint(buf, -100) # geometry-x (absolute)
write_sint(buf, 200) # geometry-y (absolute)
write_uint(buf, 16) # XYRELATIVE record
write_uint(buf, 26) # CTRAPEZOID record
write_byte(buf, 0b0000_1000) # TWHX_YRDL
write_sint(buf, 400) # geometry-y (relative)
write_uint(buf, 14) # CELL record (explicit)
write_bstring(buf, b'B') # Cell name
# Shouldn't access (undefined) width modal, despite not having a width.
write_uint(buf, 26) # CTRAPEZOID record
write_byte(buf, 0b1011_1011) # TWHX_YRDL
write_uint(buf, 1) # layer
write_uint(buf, 2) # datatype
write_uint(buf, 20) # ctrapezoid type
write_uint(buf, 200) # height
write_sint(buf, -100) # geometry-x (absolute)
write_sint(buf, 200) # geometry-y (absolute)
write_uint(buf, 16) # XYRELATIVE record
write_uint(buf, 26) # CTRAPEZOID record
write_byte(buf, 0b0000_1000) # TWHX_YRDL
write_sint(buf, 400) # geometry-y (relative)
buf.write(FOOTER)
return buf
def test_file_2() -> None:
buf = write_file_2(BytesIO())
buf.seek(0)
layout = OasisLayout.read(buf)
base_tests(layout)
assert len(layout.cells) == 2
assert layout.cells[0].name.string == 'A'
assert layout.cells[1].name.string == 'B'
assert not layout.cells[0].properties
assert not layout.cells[1].properties
for ii, cc in enumerate(layout.cells):
for jj, gg in enumerate(cc.geometry):
msg = f'Fail in cell {ii}, ctrapezoid {jj}'
assert not gg.properties, msg
assert gg.layer == 1, msg
assert gg.datatype == 2, msg
assert gg.x == -100, msg
geometry = layout.cells[0].geometry
assert geometry[0].width == 200
assert geometry[1].width == 200
assert geometry[0].ctrapezoid_type == 16
assert geometry[1].ctrapezoid_type == 16
assert geometry[0].y == 200
assert geometry[1].y == 600
geometry = layout.cells[1].geometry
assert geometry[0].height == 200
assert geometry[1].height == 200
assert geometry[0].ctrapezoid_type == 20
assert geometry[1].ctrapezoid_type == 20
assert geometry[0].y == 200
assert geometry[1].y == 600

View File

@ -1,179 +0,0 @@
from typing import IO
from io import BytesIO
import struct
from .utils import MAGIC_BYTES, FOOTER
from ..basic import write_uint, write_bstring
from ..main import OasisLayout
def base_tests(layout: OasisLayout) -> None:
assert layout.version.string == '1.0'
assert layout.validation.checksum_type == 0
assert not layout.properties
assert not layout.cells
assert not layout.cellnames
assert not layout.propnames
assert not layout.xnames
assert not layout.textstrings
assert not layout.propstrings
assert not layout.layers
def write_file_1(buf: IO[bytes]) -> IO[bytes]:
"""
File contains one PAD record.
1000 units/micron
Offset table inside START.
"""
buf.write(MAGIC_BYTES)
write_uint(buf, 1) # START record
write_bstring(buf, b'1.0') # version
write_uint(buf, 0) # dbu real type: uint
write_uint(buf, 1000) # dbu value: 1000 per micron
write_uint(buf, 0) # offset table is present here
for _ in range(6):
write_uint(buf, 0) # offset table (0: not strict)
write_uint(buf, 0) # offset table (0: no entry present)
write_uint(buf, 0) # PAD record
buf.write(FOOTER)
return buf
def test_file_1() -> None:
buf = write_file_1(BytesIO())
buf.seek(0)
layout = OasisLayout.read(buf)
base_tests(layout)
assert layout.unit == 1000
def write_file_2(buf: IO[bytes]) -> IO[bytes]:
"""
File contains no records.
1/2 unit/micron
Offset table inside START.
"""
buf.write(MAGIC_BYTES)
write_uint(buf, 1) # START record
write_bstring(buf, b'1.0') # version
write_uint(buf, 2) # dbu real type: fraction 1/x
write_uint(buf, 2) # dbu value: 1/2 per micron
write_uint(buf, 0) # offset table is present here
for _ in range(6):
write_uint(buf, 0) # offset table (0: not strict)
write_uint(buf, 0) # offset table (0: no entry present)
buf.write(FOOTER)
return buf
def test_file_2() -> None:
buf = write_file_2(BytesIO())
buf.seek(0)
layout = OasisLayout.read(buf)
base_tests(layout)
assert layout.unit == 0.5
def write_file_3(buf: IO[bytes]) -> IO[bytes]:
"""
File contains no records.
10/4 unit/micron
Offset table inside START.
"""
buf.write(MAGIC_BYTES)
write_uint(buf, 1) # START record
write_bstring(buf, b'1.0') # version
write_uint(buf, 4) # dbu real type: fraction a/b
write_uint(buf, 10) # dbu value a
write_uint(buf, 4) # dbu value b: 10/4 per micron
write_uint(buf, 0) # offset table is present here
for _ in range(6):
write_uint(buf, 0) # offset table (0: not strict)
write_uint(buf, 0) # offset table (0: no entry present)
buf.write(FOOTER)
return buf
def test_file_3() -> None:
buf = write_file_3(BytesIO())
buf.seek(0)
layout = OasisLayout.read(buf)
base_tests(layout)
assert layout.unit == 10 / 4
def write_file_4(buf: IO[bytes]) -> IO[bytes]:
"""
File contains no records.
12.5 unit/micron (float32)
Offset table inside START.
"""
buf.write(MAGIC_BYTES)
write_uint(buf, 1) # START record
write_bstring(buf, b'1.0') # version
write_uint(buf, 6) # dbu real type: float32
buf.write(struct.pack("<f", 12.5)) # dbu value: 12.5
write_uint(buf, 0) # offset table is present here
for _ in range(6):
write_uint(buf, 0) # offset table (0: not strict)
write_uint(buf, 0) # offset table (0: no entry present)
buf.write(FOOTER)
return buf
def test_file_4() -> None:
buf = write_file_4(BytesIO())
buf.seek(0)
layout = OasisLayout.read(buf)
base_tests(layout)
assert layout.unit == 12.5
def write_file_5(buf: IO[bytes]) -> IO[bytes]:
"""
File contains no records.
12.5 unit/micron (float64)
Offset table inside START.
"""
buf.write(MAGIC_BYTES)
write_uint(buf, 1) # START record
write_bstring(buf, b'1.0') # version
write_uint(buf, 7) # dbu real type: float64
buf.write(struct.pack("<d", 12.5)) # dbu value: 12.5
write_uint(buf, 0) # offset table is present here
for _ in range(6):
write_uint(buf, 0) # offset table (0: not strict)
write_uint(buf, 0) # offset table (0: no entry present)
buf.write(FOOTER)
return buf
def test_file_5() -> None:
buf = write_file_5(BytesIO())
buf.seek(0)
layout = OasisLayout.read(buf)
base_tests(layout)
assert layout.unit == 12.5

View File

@ -1,335 +0,0 @@
from typing import IO
from collections.abc import Sequence
from io import BytesIO
from .utils import HEADER, FOOTER
from ..basic import write_uint, write_sint, write_bstring, write_byte
from ..main import OasisLayout
LAYERS = [
(1, 2), (1, 5), (1, 6), (1, 8),
(5, 2), (5, 5), (5, 6), (5, 8),
(6, 2), (6, 5), (6, 6), (6, 8),
(7, 2), (7, 5), (7, 6), (7, 8),
]
def base_tests(layout: OasisLayout) -> None:
assert layout.version.string == '1.0'
assert layout.unit == 1000
assert layout.validation.checksum_type == 0
assert not layout.properties
assert not layout.propnames
assert not layout.xnames
assert not layout.textstrings
assert not layout.cellnames
assert len(layout.cells) == 1
assert layout.cells[0].name.string == 'A' # type: ignore
assert not layout.cells[0].properties
def write_names_geom(buf: IO[bytes], short: bool = False) -> IO[bytes]:
write_uint(buf, 11) # LAYERNAME record (geometry)
write_bstring(buf, b'AA') # name
write_uint(buf, 0) # all layers
write_uint(buf, 0) # all datatypes
write_uint(buf, 11) # LAYERNAME record (geometry)
write_bstring(buf, b'L5A') # name
write_uint(buf, 1) # layer <=5
write_uint(buf, 5) # (...)
write_uint(buf, 0) # all datatypes
write_uint(buf, 11) # LAYERNAME record (geometry)
write_bstring(buf, b'H5A') # name
write_uint(buf, 2) # layer >=5
write_uint(buf, 5) # (...)
write_uint(buf, 0) # all datatypes
write_uint(buf, 11) # LAYERNAME record (geometry)
write_bstring(buf, b'E5A') # name
write_uint(buf, 3) # layer ==5
write_uint(buf, 5) # (...)
write_uint(buf, 0) # all datatypes
write_uint(buf, 11) # LAYERNAME record (geometry)
write_bstring(buf, b'I56A') # name
write_uint(buf, 4) # layer 5 to 6
write_uint(buf, 5) # (...)
write_uint(buf, 6) # (...)
write_uint(buf, 0) # all datatypes
if short:
return buf
write_uint(buf, 11) # LAYERNAME record (geometry)
write_bstring(buf, b'E5L4') # name
write_uint(buf, 3) # layer ==5
write_uint(buf, 5) # (...)
write_uint(buf, 1) # datatype <=4
write_uint(buf, 4) # (...)
write_uint(buf, 11) # LAYERNAME record (geometry)
write_bstring(buf, b'E5H4') # name
write_uint(buf, 3) # layer ==5
write_uint(buf, 5) # (...)
write_uint(buf, 2) # datatype >=4
write_uint(buf, 4) # (...)
write_uint(buf, 11) # LAYERNAME record (geometry)
write_bstring(buf, b'E5E4') # name
write_uint(buf, 3) # layer ==5
write_uint(buf, 5) # (...)
write_uint(buf, 3) # datatype ==4
write_uint(buf, 4) # (...)
write_uint(buf, 11) # LAYERNAME record (geometry)
write_bstring(buf, b'E5I47') # name
write_uint(buf, 3) # layer ==5
write_uint(buf, 5) # (...)
write_uint(buf, 4) # datatype 4 to 7
write_uint(buf, 4) # (...)
write_uint(buf, 7) # (...)
return buf
def write_names_text(buf: IO[bytes], prefix: bytes = b'') -> IO[bytes]:
write_uint(buf, 12) # LAYERNAME record (geometry)
write_bstring(buf, prefix + b'AA') # name
write_uint(buf, 0) # all layers
write_uint(buf, 0) # all datatypes
write_uint(buf, 12) # LAYERNAME record (geometry)
write_bstring(buf, prefix + b'L5A') # name
write_uint(buf, 1) # layer <=5
write_uint(buf, 5) # (...)
write_uint(buf, 0) # all datatypes
write_uint(buf, 12) # LAYERNAME record (geometry)
write_bstring(buf, prefix + b'H5A') # name
write_uint(buf, 2) # layer >=5
write_uint(buf, 5) # (...)
write_uint(buf, 0) # all datatypes
write_uint(buf, 12) # LAYERNAME record (geometry)
write_bstring(buf, prefix + b'E5A') # name
write_uint(buf, 3) # layer ==5
write_uint(buf, 5) # (...)
write_uint(buf, 0) # all datatypes
write_uint(buf, 12) # LAYERNAME record (geometry)
write_bstring(buf, prefix + b'I56A') # name
write_uint(buf, 4) # layer 5 to 6
write_uint(buf, 5) # (...)
write_uint(buf, 6) # (...)
write_uint(buf, 0) # all datatypes
return buf
def write_geom(buf: IO[bytes]) -> IO[bytes]:
for ll, dt in LAYERS:
write_uint(buf, 27) # CIRCLE record
write_byte(buf, 0b0011_1011) # 00rX_YRDL
write_uint(buf, ll) # layer
write_uint(buf, dt) # datatype
write_uint(buf, 150) # radius
write_sint(buf, ll * 1000) # geometry-x (absolute)
write_sint(buf, dt * 1000) # geometry-y (absolute)
return buf
def write_text(buf: IO[bytes]) -> IO[bytes]:
for ll, dt in LAYERS:
write_uint(buf, 19) # TEXT record
write_byte(buf, 0b0101_1011) # 0CNX_YRTL
write_bstring(buf, b'A') # text-string
write_uint(buf, ll) # text-layer
write_uint(buf, dt) # text-datatype
write_sint(buf, ll * 1000) # geometry-x
write_sint(buf, dt * 1000) # geometry-y
return buf
def name_test(layers: Sequence, is_textlayer: bool) -> None:
for ii, nn in enumerate(layers):
msg = f'Fail on layername {ii}'
assert is_textlayer == nn.is_textlayer, msg
assert nn.nstring.string == ['AA', 'L5A', 'H5A', 'E5A', 'I56A',
'E5L4', 'E5H4', 'E5E4', 'E5I47'][ii], msg
assert nn.layer_interval[0] == [None, None, 5, 5, 5, 5, 5, 5, 5][ii], msg
assert nn.layer_interval[1] == [None, 5, None, 5, 6, 5, 5, 5, 5][ii], msg
assert nn.type_interval[0] == [None, None, None, None, None, None, 4, 4, 4][ii], msg
assert nn.type_interval[1] == [None, None, None, None, None, 4, None, 4, 7][ii], msg
def name_test_text(layers: Sequence) -> None:
for ii, nn in enumerate(layers):
msg = f'Fail on layername {ii}'
assert nn.is_textlayer, msg
assert nn.nstring.string == ['TAA', 'TL5A', 'TH5A', 'TE5A', 'TI56A'][ii], msg
assert nn.layer_interval[0] == [None, None, 5, 5, 5][ii], msg
assert nn.layer_interval[1] == [None, 5, None, 5, 6][ii], msg
assert nn.type_interval[0] == [None, None, None, None, None][ii], msg
assert nn.type_interval[1] == [None, None, None, None, None][ii], msg
def elem_test_geom(geometry: Sequence) -> None:
for ii, gg in enumerate(geometry):
msg = f'Failed on circle ({ii})'
assert gg.x == 1000 * LAYERS[ii][0], msg
assert gg.y == 1000 * LAYERS[ii][1], msg
assert gg.radius == 150, msg
assert gg.layer == LAYERS[ii][0], msg
assert gg.datatype == LAYERS[ii][1], msg
assert gg.repetition is None, msg
assert not gg.properties, msg
def elem_test_text(geometry: Sequence) -> None:
for ii, gg in enumerate(geometry):
msg = f'Failed on text ({ii})'
assert gg.x == 1000 * LAYERS[ii][0], msg
assert gg.y == 1000 * LAYERS[ii][1], msg
assert gg.string.string == 'A', msg
assert gg.layer == LAYERS[ii][0], msg
assert gg.datatype == LAYERS[ii][1], msg
assert gg.repetition is None, msg
assert not gg.properties, msg
def write_file_1(buf: IO[bytes]) -> IO[bytes]:
"""
"""
buf.write(HEADER)
write_names_geom(buf)
write_uint(buf, 14) # CELL record (explicit)
write_bstring(buf, b'A') # Cell name
write_geom(buf)
buf.write(FOOTER)
return buf
def test_file_1() -> None:
buf = write_file_1(BytesIO())
buf.seek(0)
layout = OasisLayout.read(buf)
base_tests(layout)
geometry = layout.cells[0].geometry
assert len(geometry) == len(LAYERS)
elem_test_geom(geometry)
assert len(layout.layers) == 9
name_test(layout.layers, is_textlayer=False)
def write_file_2(buf: IO[bytes]) -> IO[bytes]:
"""
"""
buf.write(HEADER)
write_names_text(buf)
write_uint(buf, 14) # CELL record (explicit)
write_bstring(buf, b'A') # Cell name
write_text(buf)
buf.write(FOOTER)
return buf
def test_file_2() -> None:
buf = write_file_2(BytesIO())
buf.seek(0)
layout = OasisLayout.read(buf)
base_tests(layout)
geometry = layout.cells[0].geometry
assert len(geometry) == len(LAYERS)
elem_test_text(geometry)
assert len(layout.layers) == 5
name_test(layout.layers, is_textlayer=True)
def write_file_3(buf: IO[bytes]) -> IO[bytes]:
"""
"""
buf.write(HEADER)
write_names_text(buf, prefix=b'T')
write_names_geom(buf, short=True)
write_uint(buf, 14) # CELL record (explicit)
write_bstring(buf, b'A') # Cell name
write_text(buf)
write_geom(buf)
buf.write(FOOTER)
return buf
def write_file_4(buf: IO[bytes]) -> IO[bytes]:
"""
"""
buf.write(HEADER)
write_uint(buf, 14) # CELL record (explicit)
write_bstring(buf, b'A') # Cell name
write_text(buf)
write_geom(buf)
write_names_text(buf, prefix=b'T')
write_names_geom(buf, short=True)
buf.write(FOOTER)
return buf
def test_file_3() -> None:
buf = write_file_3(BytesIO())
buf.seek(0)
layout = OasisLayout.read(buf)
base_tests(layout)
geometry = layout.cells[0].geometry
assert len(geometry) == 2 * len(LAYERS)
elem_test_text(geometry[:len(LAYERS)])
elem_test_geom(geometry[len(LAYERS):])
assert len(layout.layers) == 2 * 5
name_test_text(layout.layers[:5])
name_test(layout.layers[5:], is_textlayer=False)
def test_file_4() -> None:
buf = write_file_4(BytesIO())
buf.seek(0)
layout = OasisLayout.read(buf)
base_tests(layout)
geometry = layout.cells[0].geometry
assert len(geometry) == 2 * len(LAYERS)
elem_test_text(geometry[:len(LAYERS)])
elem_test_geom(geometry[len(LAYERS):])
assert len(layout.layers) == 2 * 5
name_test_text(layout.layers[:5])
name_test(layout.layers[5:], is_textlayer=False)

View File

@ -1,252 +0,0 @@
# mypy: disable-error-code="union-attr"
from typing import IO
from io import BytesIO
from .utils import HEADER, FOOTER
from ..basic import write_uint, write_sint, write_bstring, write_byte
from ..main import OasisLayout
def base_tests(layout: OasisLayout) -> None:
assert layout.version.string == '1.0'
assert layout.unit == 1000
assert layout.validation.checksum_type == 0
assert not layout.properties
assert not layout.propnames
assert not layout.xnames
assert not layout.textstrings
assert not layout.cellnames
assert not layout.layers
def write_file_1(buf: IO[bytes]) -> IO[bytes]:
"""
"""
buf.write(HEADER)
write_uint(buf, 14) # CELL record (explicit)
write_bstring(buf, b'A') # Cell name
# RECTANGLE 0
write_uint(buf, 20) # RECTANGLE record
write_byte(buf, 0b0110_0011) # SWHX_YRDL
write_uint(buf, 1) # layer
write_uint(buf, 2) # datatype
write_uint(buf, 10) # width
write_uint(buf, 20) # height
# TEXT 1
write_uint(buf, 19) # TEXT record
write_byte(buf, 0b0100_0011) # 0CNX_YRTL
write_bstring(buf, b'A') # text string
write_uint(buf, 2) # layer
write_uint(buf, 1) # datatype
# RECTANGLE 2
write_uint(buf, 20) # RECTANGLE record
write_byte(buf, 0b0001_1000) # SWHX_YRDL
write_sint(buf, 100) # geometry-x (absolute)
write_sint(buf, -100) # geometry-y (absolute)
# TEXT 3
write_uint(buf, 19) # TEXT record
write_byte(buf, 0b0001_1000) # 0CNX_YRTL
write_sint(buf, 100) # text-x (absolute)
write_sint(buf, -100) # text-y (absolute)
# RECTANGLE 4
write_uint(buf, 20) # RECTANGLE record
write_byte(buf, 0b0001_1000) # SWHX_YRDL
write_sint(buf, 200) # geometry-x (absolute)
write_sint(buf, -200) # geometry-y (absolute)
# TEXT 5
write_uint(buf, 19) # TEXT record
write_byte(buf, 0b0001_1000) # 0CNX_YRTL
write_sint(buf, 200) # text-x (absolute)
write_sint(buf, -200) # text-y (absolute)
write_uint(buf, 16) # XYRELATIVE record
# RECTANGLE 6
write_uint(buf, 20) # RECTANGLE record
write_byte(buf, 0b0001_1000) # SWHX_YRDL
write_sint(buf, 100) # geometry-x (relative)
write_sint(buf, -100) # geometry-y (relative)
# TEXT 7
write_uint(buf, 19) # TEXT record
write_byte(buf, 0b0001_1000) # 0CNX_YRTL
write_sint(buf, 100) # text-x (relative)
write_sint(buf, -100) # text-y (relative)
write_uint(buf, 14) # CELL record (explicit)
write_bstring(buf, b'B') # Cell name
# RECTANGLE 0
write_uint(buf, 20) # RECTANGLE record
write_byte(buf, 0b0110_0011) # SWHX_YRDL
write_uint(buf, 1) # layer
write_uint(buf, 2) # datatype
write_uint(buf, 20) # width
write_uint(buf, 10) # height
# TEXT 1
write_uint(buf, 19) # TEXT record
write_byte(buf, 0b0100_0011) # 0CNX_YRTL
write_bstring(buf, b'B') # text string
write_uint(buf, 2) # layer
write_uint(buf, 1) # datatype
# RECTANGLE 2
write_uint(buf, 20) # RECTANGLE record
write_byte(buf, 0b0001_1000) # SWHX_YRDL
write_sint(buf, 100) # geometry-x (absolute)
write_sint(buf, 100) # geometry-y (absolute)
# TEXT 3
write_uint(buf, 19) # TEXT record
write_byte(buf, 0b0001_1000) # 0CNX_YRTL
write_sint(buf, 100) # text-x (absolute)
write_sint(buf, 100) # text-y (absolute)
# RECTANGLE 4
write_uint(buf, 20) # RECTANGLE record
write_byte(buf, 0b0001_1000) # SWHX_YRDL
write_sint(buf, 200) # geometry-x (absolute)
write_sint(buf, 200) # geometry-y (absolute)
# TEXT 5
write_uint(buf, 19) # TEXT record
write_byte(buf, 0b0001_1000) # 0CNX_YRTL
write_sint(buf, 200) # text-x (absolute)
write_sint(buf, 200) # text-y (absolute)
write_uint(buf, 16) # XYRELATIVE record
# RECTANGLE 6
write_uint(buf, 20) # RECTANGLE record
write_byte(buf, 0b0001_1000) # SWHX_YRDL
write_sint(buf, 100) # geometry-x (relative)
write_sint(buf, 100) # geometry-y (relative)
# TEXT 7
write_uint(buf, 19) # TEXT record
write_byte(buf, 0b0001_1000) # 0CNX_YRTL
write_sint(buf, 100) # text-x (relative)
write_sint(buf, 100) # text-y (relative)
# PLACEMENT 0
write_uint(buf, 17) # PLACEMENT (simple)
write_byte(buf, 0b1000_0000) # CNXY_RAAF
write_bstring(buf, b'A') # Cell reference
# PLACEMENT 1
write_uint(buf, 17) # PLACEMENT (simple)
write_byte(buf, 0b0011_0000) # CNXY_RAAF
write_sint(buf, 50) # placement-x (relative)
write_sint(buf, 50) # placement-y (relative)
write_uint(buf, 14) # CELL record (explicit)
write_bstring(buf, b'TOP') # Cell name
# PLACEMENT 0
write_uint(buf, 17) # PLACEMENT (simple)
write_byte(buf, 0b1000_0000) # CNXY_RAAF
write_bstring(buf, b'B') # Cell reference
# RECTANGLE 0
write_uint(buf, 20) # RECTANGLE record
write_byte(buf, 0b0110_0011) # SWHX_YRDL
write_uint(buf, 1) # layer
write_uint(buf, 2) # datatype
write_uint(buf, 50) # width
write_uint(buf, 5) # height
# TEXT 1
write_uint(buf, 19) # TEXT record
write_byte(buf, 0b0100_0011) # 0CNX_YRTL
write_bstring(buf, b'TOP') # text string
write_uint(buf, 2) # layer
write_uint(buf, 1) # datatype
buf.write(FOOTER)
return buf
def test_file_1() -> None:
buf = write_file_1(BytesIO())
buf.seek(0)
layout = OasisLayout.read(buf)
base_tests(layout)
assert len(layout.cells) == 3
assert layout.cells[0].name.string == 'A'
assert layout.cells[1].name.string == 'B'
assert layout.cells[2].name.string == 'TOP'
assert not layout.cells[0].properties
assert not layout.cells[1].properties
assert not layout.cells[2].properties
geometry = layout.cells[0].geometry
assert len(geometry) == 8
for ii, gg in enumerate(geometry):
msg = f'Failed on geometry {ii} in cell A'
if ii % 2 == 0:
assert gg.width == 10, msg
assert gg.height == 20, msg
assert gg.layer == 1, msg
assert gg.datatype == 2, msg
else:
assert gg.string.string == 'A', msg
assert gg.layer == 2, msg
assert gg.datatype == 1, msg
assert not gg.properties, msg
assert gg.x == (ii // 2) * 100, msg
assert gg.y == (ii // 2) * -100, msg
geometry = layout.cells[1].geometry
assert len(geometry) == 8
for ii, gg in enumerate(geometry):
msg = f'Failed on geometry {ii} in cell B'
if ii % 2 == 0:
assert gg.width == 20, msg
assert gg.height == 10, msg
assert gg.layer == 1, msg
assert gg.datatype == 2, msg
else:
assert gg.string.string == 'B', msg
assert gg.layer == 2, msg
assert gg.datatype == 1, msg
assert not gg.properties, msg
assert gg.x == (ii // 2) * 100, msg
assert gg.y == (ii // 2) * 100, msg
assert layout.cells[1].placements[0].name.string == 'A'
assert layout.cells[1].placements[1].name.string == 'A'
assert layout.cells[1].placements[0].x == 0
assert layout.cells[1].placements[0].y == 0
assert layout.cells[1].placements[1].x == 50
assert layout.cells[1].placements[1].y == 50
assert layout.cells[2].placements[0].name.string == 'B'
assert layout.cells[2].placements[0].x == 0
assert layout.cells[2].placements[0].y == 0
assert layout.cells[2].geometry[0].layer == 1
assert layout.cells[2].geometry[0].datatype == 2
assert layout.cells[2].geometry[0].width == 50
assert layout.cells[2].geometry[0].height == 5
assert layout.cells[2].geometry[0].x == 0
assert layout.cells[2].geometry[0].y == 0
assert layout.cells[2].geometry[1].layer == 2
assert layout.cells[2].geometry[1].datatype == 1
assert layout.cells[2].geometry[1].string.string == 'TOP'
assert layout.cells[2].geometry[1].x == 0
assert layout.cells[2].geometry[1].y == 0

View File

@ -1,199 +0,0 @@
# mypy: disable-error-code="union-attr"
from typing import IO
from io import BytesIO
from numpy.testing import assert_equal
from .utils import HEADER, FOOTER
from ..basic import write_uint, write_sint, write_bstring, write_byte, PathExtensionScheme
from ..main import OasisLayout
def base_tests(layout: OasisLayout) -> None:
assert layout.version.string == '1.0'
assert layout.unit == 1000
assert layout.validation.checksum_type == 0
assert not layout.properties
assert not layout.propnames
assert not layout.xnames
assert not layout.textstrings
assert not layout.cellnames
assert not layout.layers
assert len(layout.cells) == 1
assert layout.cells[0].name.string == 'ABC'
assert not layout.cells[0].properties
def write_file_1(buf: IO[bytes]) -> IO[bytes]:
"""
"""
buf.write(HEADER)
write_uint(buf, 14) # CELL record (explicit)
write_bstring(buf, b'ABC') # Cell name
# PATH 0
write_uint(buf, 22) # PATH record
write_byte(buf, 0b1111_1011) # EWPX_YRDL
write_uint(buf, 1) # layer
write_uint(buf, 2) # datatype
write_uint(buf, 10) # half-width
write_byte(buf, 0b0000_1111) # extension-scheme 0000_SSEE
write_sint(buf, 5) # (extension-scheme) start
write_sint(buf, -5) # (extension-scheme) end
write_uint(buf, 0) # pointlist: 1-delta, horiz-fisrt
write_uint(buf, 3) # (pointlist) dimension
write_sint(buf, 150) # (pointlist)
write_sint(buf, 50) # (pointlist)
write_sint(buf, -50) # (pointlist)
write_sint(buf, 0) # geometry-x (absolute)
write_sint(buf, 100) # geometry-y (absolute)
write_uint(buf, 16) # XYRELATIVE record
# PATH 1
write_uint(buf, 22) # PATH record
write_byte(buf, 0b1110_1011) # EWPX_YRDL
write_uint(buf, 1) # layer
write_uint(buf, 2) # datatype
write_uint(buf, 10) # half-width
write_byte(buf, 0b0000_0000) # extension-scheme 0000_SSEE
write_uint(buf, 0) # pointlist: 1-delta, horiz-fisrt
write_uint(buf, 3) # (pointlist) dimension
write_sint(buf, 150) # (pointlist)
write_sint(buf, 50) # (pointlist)
write_sint(buf, -50) # (pointlist)
write_sint(buf, 200) # geometry-y (relative)
# PATH 2
write_uint(buf, 22) # PATH record
write_byte(buf, 0b1110_1001) # EWPX_YRDL
write_uint(buf, 1) # layer
write_uint(buf, 10) # half-width
write_byte(buf, 0b0000_0100) # extension-scheme 0000_SSEE
write_uint(buf, 0) # pointlist: 1-delta, horiz-fisrt
write_uint(buf, 3) # (pointlist) dimension
write_sint(buf, 150) # (pointlist)
write_sint(buf, 50) # (pointlist)
write_sint(buf, -50) # (pointlist)
write_sint(buf, 200) # geometry-y (relative)
# PATH 3
write_uint(buf, 22) # PATH record
write_byte(buf, 0b1110_1010) # EWPX_YRDL
write_uint(buf, 2) # datatype
write_uint(buf, 12) # half-width
write_byte(buf, 0b0000_0101) # extension-scheme 0000_SSEE
write_uint(buf, 0) # pointlist: 1-delta, horiz-fisrt
write_uint(buf, 3) # (pointlist) dimension
write_sint(buf, 150) # (pointlist)
write_sint(buf, 50) # (pointlist)
write_sint(buf, -50) # (pointlist)
write_sint(buf, 200) # geometry-y (relative)
# PATH 4
write_uint(buf, 22) # PATH record
write_byte(buf, 0b1010_1011) # EWPX_YRDL
write_uint(buf, 1) # layer
write_uint(buf, 2) # datatype
write_byte(buf, 0b0000_1010) # extension-scheme 0000_SSEE
write_uint(buf, 0) # pointlist: 1-delta, horiz-fisrt
write_uint(buf, 3) # (pointlist) dimension
write_sint(buf, 150) # (pointlist)
write_sint(buf, 50) # (pointlist)
write_sint(buf, -50) # (pointlist)
write_sint(buf, 200) # geometry-y (relative)
# PATH 5
write_uint(buf, 22) # PATH record
write_byte(buf, 0b0000_1011) # EWPX_YRDL
write_uint(buf, 2) # layer
write_uint(buf, 3) # datatype
write_sint(buf, 200) # geometry-y (relative)
# PATH 6
write_uint(buf, 22) # PATH record
write_byte(buf, 0b0000_1111) # EWPX_YRDL
write_uint(buf, 2) # layer
write_uint(buf, 3) # datatype
write_sint(buf, 200) # geometry-y (relative)
write_uint(buf, 1) # repetition (3x4 matrix)
write_uint(buf, 1) # (repetition) x-dimension
write_uint(buf, 2) # (repetition) y-dimension
write_uint(buf, 200) # (repetition) x-spacing
write_uint(buf, 300) # (repetition) y-spacing
write_uint(buf, 16) # XYRELATIVE record
# PATH 7
write_uint(buf, 22) # PATH record
write_byte(buf, 0b0001_0101) # EWPX_YRDL
write_uint(buf, 1) # layer
write_sint(buf, 1000) # geometry-x (relative)
write_uint(buf, 0) # repetition (reuse)
buf.write(FOOTER)
return buf
def test_file_1() -> None:
buf = write_file_1(BytesIO())
buf.seek(0)
layout = OasisLayout.read(buf)
base_tests(layout)
geometry = layout.cells[0].geometry
assert len(geometry) == 8
for ii, gg in enumerate(geometry):
msg = f'Failed on path {ii}'
if ii < 7:
assert gg.y == 100 + ii * 200, msg
assert gg.x == 0, msg
else:
assert gg.x == 1000, msg
assert gg.y == 1300, msg
if ii < 5:
assert gg.layer == 1, msg
assert gg.datatype == 2, msg
elif ii < 7:
assert gg.layer == 2, msg
assert gg.datatype == 3, msg
else:
assert gg.layer == 1, msg
assert gg.datatype == 3, msg
if ii < 6:
assert gg.repetition is None, msg
elif ii in (7, 8):
assert gg.repetition.a_count == 3, msg
assert gg.repetition.b_count == 4, msg
assert gg.repetition.a_vector == [200, 0], msg
assert gg.repetition.b_vector == [0, 300], msg
assert not gg.properties, msg
if ii < 3:
assert gg.half_width == 10, msg
else:
assert gg.half_width == 12, msg
assert len(gg.point_list) == 3, msg # type: ignore
assert_equal(gg.point_list, [[150, 0], [0, 50], [-50, 0]], err_msg=msg)
if ii >= 4:
assert gg.extension_start == (PathExtensionScheme.HalfWidth, None)
assert gg.extension_end == (PathExtensionScheme.HalfWidth, None)
assert geometry[0].extension_start == (PathExtensionScheme.Arbitrary, 5)
assert geometry[1].extension_start == (PathExtensionScheme.Arbitrary, 5)
assert geometry[2].extension_start == (PathExtensionScheme.Flush, None)
assert geometry[3].extension_start == (PathExtensionScheme.Flush, None)
assert geometry[0].extension_end == (PathExtensionScheme.Arbitrary, -5)
assert geometry[1].extension_end == (PathExtensionScheme.Arbitrary, -5)
assert geometry[2].extension_end == (PathExtensionScheme.Arbitrary, -5)
assert geometry[3].extension_end == (PathExtensionScheme.Flush, None)

View File

@ -1,890 +0,0 @@
# mypy: disable-error-code="union-attr"
from typing import IO, cast
from io import BytesIO
from numpy.testing import assert_equal
from .utils import HEADER, FOOTER
from ..basic import write_uint, write_sint, write_bstring, write_byte, write_float32, write_float64
from ..records import Rectangle
from ..main import OasisLayout
def base_tests(layout: OasisLayout) -> None:
assert layout.version.string == '1.0'
assert layout.unit == 1000
assert layout.validation.checksum_type == 0
assert not layout.properties
assert not layout.propnames
assert not layout.xnames
assert not layout.textstrings
assert not layout.layers
def write_rectangle(buf: IO[bytes], pos: tuple[int, int] = (300, -400)) -> None:
write_uint(buf, 20) # RECTANGLE record
write_byte(buf, 0b0111_1011) # SWHX_YRDL
write_uint(buf, 1) # layer
write_uint(buf, 2) # datatype
write_uint(buf, 100) # width
write_uint(buf, 200) # height
write_sint(buf, pos[0]) # geometry-x (absolute)
write_sint(buf, pos[1]) # geometry-y (absolute)
def write_file_1(buf: IO[bytes]) -> IO[bytes]:
"""
"""
buf.write(HEADER)
write_uint(buf, 14) # CELL record (explicit)
write_bstring(buf, b'A') # Cell name
write_rectangle(buf)
write_uint(buf, 14) # CELL record (explicit)
write_bstring(buf, b'TOP') # Cell name
write_uint(buf, 16) # XYRELATIVE record
# PLACEMENT 0
write_uint(buf, 17) # PLACEMENT (simple)
write_byte(buf, 0b1011_0000) # CNXY_RAAF
write_bstring(buf, b'A') # cell reference
write_sint(buf, -300) # placement-x (relative)
write_sint(buf, 400) # placement-y (relative)
# PLACEMENT 1
write_uint(buf, 17) # PLACEMENT (simple)
write_byte(buf, 0b0011_0000) # CNXY_RAAF
write_sint(buf, 0) # placement-x (relative)
write_sint(buf, 400) # placement-y (relative)
# PLACEMENT 2
write_uint(buf, 17) # PLACEMENT (simple)
write_byte(buf, 0b0001_0000) # CNXY_RAAF
write_sint(buf, 400) # placement-y (relative)
# PLACEMENT 3
write_uint(buf, 17) # PLACEMENT (simple)
write_byte(buf, 0b0010_0000) # CNXY_RAAF
write_sint(buf, 300) # placement-x (relative)
write_uint(buf, 15) # XYABSOLUTE record
# PLACEMENT 4
write_uint(buf, 17) # PLACEMENT (simple)
write_byte(buf, 0b0011_0001) # CNXY_RAAF
write_sint(buf, 700) # placement-x (absolute)
write_sint(buf, 400) # placement-y (absolute)
write_uint(buf, 16) # XYRELATIVE record
# PLACEMENT 5
write_uint(buf, 17) # PLACEMENT (simple)
write_byte(buf, 0b0001_0010) # CNXY_RAAF
write_sint(buf, 1000) # placement-y (relative)
# PLACEMENT 6
write_uint(buf, 17) # PLACEMENT (simple)
write_byte(buf, 0b0001_0011) # CNXY_RAAF
write_sint(buf, 1000) # placement-y (relative)
write_uint(buf, 15) # XYABSOLUTE record
# PLACEMENT 7
write_uint(buf, 17) # PLACEMENT (simple)
write_byte(buf, 0b0011_1111) # CNXY_RAAF
write_sint(buf, 2000) # placement-x (absolute)
write_sint(buf, 0) # placement-y (absolute)
write_uint(buf, 1) # repetition (3x4 matrix)
write_uint(buf, 1) # (repetition) x-dimension
write_uint(buf, 2) # (repetition) y-dimension
write_uint(buf, 300) # (repetition) x-spacing
write_uint(buf, 300) # (repetition) y-spacing
write_uint(buf, 16) # XYRELATIVE record
# PLACEMENT 8
write_uint(buf, 17) # PLACEMENT (simple)
write_byte(buf, 0b0011_1111) # CNXY_RAAF
write_sint(buf, 2000) # placement-x (relative)
write_sint(buf, 0) # placement-y (relative)
write_uint(buf, 0) # repetition (reuse)
# PLACEMENT 9
write_uint(buf, 17) # PLACEMENT (simple)
write_byte(buf, 0b0011_1111) # CNXY_RAAF
write_sint(buf, 2000) # placement-x (relative)
write_sint(buf, 0) # placement-y (relative)
write_uint(buf, 2) # repetition (3 cols.)
write_uint(buf, 1) # (repetition) count
write_uint(buf, 320) # (repetition) spacing
# PLACEMENT 10
write_uint(buf, 17) # PLACEMENT (simple)
write_byte(buf, 0b0011_1111) # CNXY_RAAF
write_sint(buf, 2000) # placement-x (relative)
write_sint(buf, 0) # placement-y (relative)
write_uint(buf, 3) # repetition (4 rows)
write_uint(buf, 2) # (repetition) count
write_uint(buf, 310) # (repetition) spacing
# PLACEMENT 11
write_uint(buf, 17) # PLACEMENT (simple)
write_byte(buf, 0b0011_1111) # CNXY_RAAF
write_sint(buf, 2000) # placement-x (relative)
write_sint(buf, 0) # placement-y (relative)
write_uint(buf, 4) # repetition (4 arbitrary cols.)
write_uint(buf, 2) # (repetition) dimension
write_uint(buf, 320) # (repetition) spacing
write_uint(buf, 330) # (repetition) spacing
write_uint(buf, 340) # (repetition) spacing
# PLACEMENT 12
write_uint(buf, 17) # PLACEMENT (simple)
write_byte(buf, 0b0011_1111) # CNXY_RAAF
write_sint(buf, 2000) # placement-x (relative)
write_sint(buf, 0) # placement-y (relative)
write_uint(buf, 8) # repetition (3x4 matrix, arbitrary vectors)
write_uint(buf, 1) # (repetition) n-dimension
write_uint(buf, 2) # (repetition) m-dimension
write_uint(buf, 310 << 2 | 0b01) # (repetition) n-displacement g-delta: (310, 320)
write_sint(buf, 320) # (repetition g-delta)
write_uint(buf, 330 << 4 | 0b1010) # (repetition) m-displacement g-delta: 330-northwest (-330, 330)
buf.write(FOOTER)
return buf
def test_file_1() -> None:
buf = write_file_1(BytesIO())
buf.seek(0)
layout = OasisLayout.read(buf)
base_tests(layout)
assert len(layout.cells) == 2
assert layout.cells[0].name.string == 'A'
assert not layout.cells[0].properties
assert not layout.cells[0].placements
assert layout.cells[1].name.string == 'TOP'
assert not layout.cells[1].properties
assert not layout.cells[1].geometry
geometry = cast(list[Rectangle], layout.cells[0].geometry)
assert len(geometry) == 1
assert geometry[0].layer == 1
assert geometry[0].datatype == 2
assert geometry[0].width == 100
assert geometry[0].height == 200
assert geometry[0].x == 300
assert geometry[0].y == -400
placements = layout.cells[1].placements
assert len(placements) == 13
for ii, pp in enumerate(placements):
msg = f'Failed on placement {ii}'
assert not pp.properties, msg
assert pp.name.string == 'A', msg
if ii < 3:
assert pp.x == -300, msg
elif ii == 3:
assert pp.x == 0, msg
elif 4 <= ii < 7:
assert pp.x == 700, msg
else:
assert pp.x == 2000 * (ii - 6), msg
if ii < 3:
assert pp.y == 400 * (ii + 1), msg
elif ii >= 7:
assert pp.y == 0, msg
if ii < 4 or ii == 5:
assert not bool(pp.flip), msg
else:
assert bool(pp.flip), msg
if ii < 5:
assert pp.angle == 0, msg
elif ii in (5, 6):
assert pp.angle == 90, msg
elif ii >= 7:
assert pp.angle == 270, msg
if ii < 7:
assert pp.repetition is None, msg
elif ii in (7, 8):
assert pp.repetition.a_count == 3, msg
assert pp.repetition.b_count == 4, msg
assert pp.repetition.a_vector == [300, 0], msg
assert pp.repetition.b_vector == [0, 300], msg
assert placements[3].y == 1200
assert placements[4].y == 400
assert placements[5].y == 1400
assert placements[6].y == 2400
assert placements[9].repetition.a_count == 3
assert placements[9].repetition.b_count is None
assert placements[9].repetition.a_vector == [320, 0]
assert placements[9].repetition.b_vector is None
assert placements[10].repetition.a_count == 4
assert placements[10].repetition.b_count is None
assert placements[10].repetition.a_vector == [0, 310]
assert placements[10].repetition.b_vector is None
assert_equal(placements[11].repetition.x_displacements, [320, 330, 340])
assert_equal(placements[11].repetition.y_displacements, [0, 0, 0])
assert placements[12].repetition.a_count == 3
assert placements[12].repetition.b_count == 4
assert placements[12].repetition.a_vector == [310, 320]
assert placements[12].repetition.b_vector == [-330, 330]
def write_file_common(buf: IO[bytes], variant: int) -> IO[bytes]:
"""
"""
assert variant in (2, 3, 5, 7), 'Error in test definition!'
buf.write(HEADER)
if variant in (3, 5):
write_uint(buf, 3) # CELLNAME record (implicit id 0)
write_bstring(buf, b'A')
write_uint(buf, 3) # CELLNAME record (implicit id 1)
write_bstring(buf, b'TOP')
if variant == 3:
write_uint(buf, 13) # CELL record (name ref.)
write_uint(buf, 0) # Cell name 0 (A)
write_rectangle(buf)
if variant == 2:
write_uint(buf, 14) # CELL record (explicit)
write_bstring(buf, b'TOP') # Cell name
else:
write_uint(buf, 13) # CELL record (name ref.)
write_uint(buf, 1) # Cell name 1 (TOP)
write_uint(buf, 16) # XYRELATIVE record
# PLACEMENT 0
write_uint(buf, 17) # PLACEMENT (simple)
if variant == 2:
write_byte(buf, 0b1011_0000) # CNXY_RAAF
write_bstring(buf, b'A') # cell reference
else:
write_byte(buf, 0b1111_0000) # CNXY_RAAF
write_uint(buf, 0) # cell reference
write_sint(buf, -300) # placement-x (relative)
write_sint(buf, 400) # placement-y (relative)
# PLACEMENT 1
write_uint(buf, 17) # PLACEMENT (simple)
write_byte(buf, 0b0011_0000) # CNXY_RAAF
write_sint(buf, 0) # placement-x (relative)
write_sint(buf, 400) # placement-y (relative)
# PLACEMENT 2
write_uint(buf, 17) # PLACEMENT (simple)
write_byte(buf, 0b0001_0000) # CNXY_RAAF
write_sint(buf, 400) # placement-y (relative)
# PLACEMENT 3
write_uint(buf, 17) # PLACEMENT (simple)
write_byte(buf, 0b0010_0000) # CNXY_RAAF
write_sint(buf, 300) # placement-x (relative)
write_uint(buf, 15) # XYABSOLUTE record
# PLACEMENT 4
write_uint(buf, 17) # PLACEMENT (simple)
write_byte(buf, 0b0011_0001) # CNXY_RAAF
write_sint(buf, 700) # placement-x (absolute)
write_sint(buf, 400) # placement-y (absolute)
write_uint(buf, 16) # XYRELATIVE record
# PLACEMENT 5
write_uint(buf, 17) # PLACEMENT (simple)
write_byte(buf, 0b0001_0010) # CNXY_RAAF
write_sint(buf, 1000) # placement-y (relative)
# PLACEMENT 6
write_uint(buf, 17) # PLACEMENT (simple)
write_byte(buf, 0b0001_0011) # CNXY_RAAF
write_sint(buf, 1000) # placement-y (relative)
if variant == 2:
write_uint(buf, 14) # CELL record (explicit)
write_bstring(buf, b'A') # Cell name
write_rectangle(buf)
elif variant in (5, 7):
write_uint(buf, 13) # CELL record (name ref.)
write_uint(buf, 0) # Cell name 0 (A)
write_rectangle(buf)
if variant == 7:
write_uint(buf, 3) # CELLNAME record (implicit id 0)
write_bstring(buf, b'A')
write_uint(buf, 3) # CELLNAME record (implicit id 1)
write_bstring(buf, b'TOP')
buf.write(FOOTER)
return buf
def test_file_2() -> None:
buf = write_file_common(BytesIO(), 2)
buf.seek(0)
layout = OasisLayout.read(buf)
assert len(layout.cells) == 2
assert layout.cells[0].name.string == 'TOP'
assert not layout.cells[0].properties
assert not layout.cells[0].geometry
assert layout.cells[1].name.string == 'A'
assert not layout.cells[1].properties
assert not layout.cells[1].placements
assert not layout.cellnames
common_tests(layout, 2)
def test_file_3() -> None:
buf = write_file_common(BytesIO(), 3)
buf.seek(0)
layout = OasisLayout.read(buf)
assert len(layout.cells) == 2
assert layout.cells[0].name == 0
assert not layout.cells[1].properties
assert not layout.cells[1].geometry
assert layout.cells[1].name == 1
assert not layout.cells[0].properties
assert not layout.cells[0].placements
assert len(layout.cellnames) == 2
assert layout.cellnames[0].nstring.string == 'A'
assert layout.cellnames[1].nstring.string == 'TOP'
common_tests(layout, 3)
def test_file_4() -> None:
buf = write_file_4(BytesIO())
buf.seek(0)
layout = OasisLayout.read(buf)
assert len(layout.cells) == 2
assert layout.cells[0].name == 1
assert not layout.cells[0].properties
assert not layout.cells[0].geometry
assert layout.cells[1].name == 0
assert not layout.cells[1].properties
assert not layout.cells[1].placements
assert len(layout.cellnames) == 2
assert layout.cellnames[0].nstring.string == 'A'
assert layout.cellnames[1].nstring.string == 'TOP'
common_tests(layout, 4)
for ii, pp in enumerate(layout.cells[0].placements):
msg = f'Fail on placement {ii}'
assert pp.repetition.a_count == 3, msg
assert pp.repetition.b_count == 4, msg
assert pp.repetition.a_vector == [20, 0], msg
assert pp.repetition.b_vector == [0, 30], msg
def test_file_5() -> None:
buf = write_file_common(BytesIO(), 5)
buf.seek(0)
layout = OasisLayout.read(buf)
assert len(layout.cells) == 2
assert layout.cells[0].name == 1
assert not layout.cells[0].properties
assert not layout.cells[0].geometry
assert layout.cells[1].name == 0
assert not layout.cells[1].properties
assert not layout.cells[1].placements
assert len(layout.cellnames) == 2
assert layout.cellnames[0].nstring.string == 'A'
assert layout.cellnames[1].nstring.string == 'TOP'
common_tests(layout, 5)
def test_file_7() -> None:
buf = write_file_common(BytesIO(), 7)
buf.seek(0)
layout = OasisLayout.read(buf)
assert len(layout.cells) == 2
assert layout.cells[0].name == 1
assert not layout.cells[0].properties
assert not layout.cells[0].geometry
assert layout.cells[1].name == 0
assert not layout.cells[1].properties
assert not layout.cells[1].placements
assert len(layout.cellnames) == 2
assert layout.cellnames[0].nstring.string == 'A'
assert layout.cellnames[1].nstring.string == 'TOP'
common_tests(layout, 7)
def common_tests(layout: OasisLayout, variant: int) -> None:
base_tests(layout)
if variant == 3:
geom_cell = 0
top_cell = 1
else:
geom_cell = 1
top_cell = 0
geometry = layout.cells[geom_cell].geometry
assert len(geometry) == 1
assert geometry[0].layer == 1
assert geometry[0].datatype == 2
assert geometry[0].width == 100
assert geometry[0].height == 200
assert geometry[0].x == 300
assert geometry[0].y == -400
placements = layout.cells[top_cell].placements
assert len(placements) == 7
for ii, pp in enumerate(placements):
msg = f'Failed on placement {ii}'
assert not pp.properties, msg
if variant == 2:
assert pp.name.string == 'A', msg
else:
assert pp.name == 0, msg
if ii < 3:
assert pp.x == -300, msg
elif ii == 3:
assert pp.x == 0, msg
else:
assert pp.x == 700, msg
if ii < 3:
assert pp.y == 400 * (ii + 1), msg
if ii in (4, 6):
assert bool(pp.flip), msg
else:
assert not bool(pp.flip), msg
if ii in (5, 6):
assert pp.angle == 90, msg
else:
assert pp.angle == 0, msg
if variant != 4:
assert pp.repetition is None, msg
assert placements[3].y == 1200
assert placements[4].y == 400
assert placements[5].y == 1400
assert placements[6].y == 2400
def write_file_4(buf: IO[bytes]) -> IO[bytes]:
"""
"""
buf.write(HEADER)
write_uint(buf, 3) # CELLNAME record (implicit id 0)
write_bstring(buf, b'A')
write_uint(buf, 3) # CELLNAME record (implicit id 1)
write_bstring(buf, b'TOP')
write_uint(buf, 13) # CELL record (name ref.)
write_uint(buf, 1) # Cell name 1 (TOP)
write_uint(buf, 16) # XYRELATIVE record
# PLACEMENT 0
write_uint(buf, 17) # PLACEMENT (simple)
write_byte(buf, 0b1111_1000) # CNXY_RAAF
write_uint(buf, 0) # cell reference
write_sint(buf, -300) # placement-x (relative)
write_sint(buf, 400) # placement-y (relative)
write_uint(buf, 1) # repetition (3x4 matrix)
write_uint(buf, 1) # (repetition) x-dimension
write_uint(buf, 2) # (repetition) y-dimension
write_uint(buf, 20) # (repetition) x-spacing
write_uint(buf, 30) # (repetition) y-spacing
# PLACEMENT 1
write_uint(buf, 17) # PLACEMENT (simple)
write_byte(buf, 0b0011_1000) # CNXY_RAAF
write_sint(buf, 0) # placement-x (relative)
write_sint(buf, 400) # placement-y (relative)
write_uint(buf, 0) # repetition (reuse)
# PLACEMENT 2
write_uint(buf, 17) # PLACEMENT (simple)
write_byte(buf, 0b0001_1000) # CNXY_RAAF
write_sint(buf, 400) # placement-y (relative)
write_uint(buf, 0) # repetition (reuse)
# PLACEMENT 3
write_uint(buf, 17) # PLACEMENT (simple)
write_byte(buf, 0b0010_1000) # CNXY_RAAF
write_sint(buf, 300) # placement-x (relative)
write_uint(buf, 0) # repetition (reuse)
write_uint(buf, 15) # XYABSOLUTE record
# PLACEMENT 4
write_uint(buf, 17) # PLACEMENT (simple)
write_byte(buf, 0b0011_1001) # CNXY_RAAF
write_sint(buf, 700) # placement-x (absolute)
write_sint(buf, 400) # placement-y (absolute)
write_uint(buf, 0) # repetition (reuse)
write_uint(buf, 16) # XYRELATIVE record
# PLACEMENT 5
write_uint(buf, 17) # PLACEMENT (simple)
write_byte(buf, 0b0001_1010) # CNXY_RAAF
write_sint(buf, 1000) # placement-y (relative)
write_uint(buf, 0) # repetition (reuse)
# PLACEMENT 6
write_uint(buf, 17) # PLACEMENT (simple)
write_byte(buf, 0b0001_1011) # CNXY_RAAF
write_sint(buf, 1000) # placement-y (relative)
write_uint(buf, 0) # repetition (reuse)
write_uint(buf, 13) # CELL record (name ref.)
write_uint(buf, 0) # Cell name 0 (A)
write_rectangle(buf)
buf.write(FOOTER)
return buf
def write_file_6(buf: IO[bytes]) -> IO[bytes]:
"""
"""
buf.write(HEADER)
write_uint(buf, 14) # CELL record (explicit)
write_bstring(buf, b'TOPTOP') # Cell name
write_uint(buf, 16) # XYRELATIVE record
write_uint(buf, 18) # PLACEMENT (mag 0.5, manhattan)
write_byte(buf, 0b1011_0110) # CNXY_RMAF
write_bstring(buf, b'TOP') # Cell reference
write_uint(buf, 6) # magnitude, float32
write_float32(buf, 0.5) # (magnitude)
write_uint(buf, 7) # angle, float64
write_float64(buf, 90.0) # (angle)
write_sint(buf, 100) # placement-x (relative)
write_sint(buf, 0) # placement-y (relative)
write_uint(buf, 18) # PLACEMENT (no mag, manhattan)
write_byte(buf, 0b0011_0000) # CNXY_RMAF
write_sint(buf, 100) # placement-x (relative)
write_sint(buf, 1000) # placement-y (relative)
write_uint(buf, 14) # CELL record (explicit)
write_bstring(buf, b'TOP') # Cell name
write_uint(buf, 16) # XYRELATIVE record
# PLACEMENT 0
write_uint(buf, 18) # PLACEMENT (mag 0.5, manhattan)
write_byte(buf, 0b1011_0110) # CNXY_RMAF
write_bstring(buf, b'A') # Cell reference
write_uint(buf, 6) # magnitude, float32
write_float32(buf, 0.5) # (magnitude)
write_uint(buf, 7) # angle, float64
write_float64(buf, 0.0) # (angle)
write_sint(buf, -150) # placement-x (relative)
write_sint(buf, 200) # placement-y (relative)
# PLACEMENT 1
write_uint(buf, 18) # PLACEMENT (no mag, manhattan)
write_byte(buf, 0b0011_0000) # CNXY_RMAF
write_sint(buf, -150) # placement-x (relative)
write_sint(buf, 600) # placement-y (relative)
# PLACEMENT 2
write_uint(buf, 18) # PLACEMENT (no mag, manhattan)
write_byte(buf, 0b0001_0000) # CNXY_RMAF
write_sint(buf, 400) # placement-y (relative)
# PLACEMENT 3
write_uint(buf, 18) # PLACEMENT (no mag, manhattan)
write_byte(buf, 0b0010_0000) # CNXY_RMAF
write_sint(buf, 300) # placement-x (relative)
write_uint(buf, 15) # XYABSOLUTE record
# PLACEMENT 4
write_uint(buf, 18) # PLACEMENT (no mag, manhattan)
write_byte(buf, 0b0011_0001) # CNXY_RMAF
write_sint(buf, 700) # placement-x (absolute)
write_sint(buf, 400) # placement-y (absolute)
write_uint(buf, 16) # XYRELATIVE record
# PLACEMENT 5
write_uint(buf, 18) # PLACEMENT (no mag, manhattan)
write_byte(buf, 0b0001_0010) # CNXY_RMAF
write_uint(buf, 0) # angle (uint, positive)
write_uint(buf, 90) # (angle)
write_sint(buf, 1000) # placement-y (relative)
# PLACEMENT 6
write_uint(buf, 18) # PLACEMENT (no mag, manhattan)
write_byte(buf, 0b0001_0011) # CNXY_RMAF
write_uint(buf, 1) # angle (uint, negative)
write_uint(buf, 90) # (angle)
write_sint(buf, 1000) # placement-y (relative)
write_uint(buf, 14) # CELL record (explicit)
write_bstring(buf, b'A') # Cell name
write_rectangle(buf)
buf.write(FOOTER)
return buf
def test_file_6() -> None:
buf = write_file_6(BytesIO())
buf.seek(0)
layout = OasisLayout.read(buf)
base_tests(layout)
assert len(layout.cells) == 3
assert layout.cells[0].name.string == 'TOPTOP'
assert not layout.cells[0].properties
assert not layout.cells[0].geometry
assert layout.cells[1].name.string == 'TOP'
assert not layout.cells[1].properties
assert not layout.cells[1].geometry
assert layout.cells[2].name.string == 'A'
assert not layout.cells[2].properties
assert not layout.cells[2].placements
geometry = layout.cells[2].geometry
assert len(geometry) == 1
assert geometry[0].layer == 1
assert geometry[0].datatype == 2
assert geometry[0].width == 100
assert geometry[0].height == 200
assert geometry[0].x == 300
assert geometry[0].y == -400
placements = layout.cells[1].placements
assert len(placements) == 7
for ii, pp in enumerate(placements):
msg = f'Failed on placement {ii} in cell TOP'
assert not pp.properties, msg
assert pp.name.string == 'A', msg
assert pp.flip == (ii in (4, 6)), msg
assert pp.repetition is None, msg
if ii == 0:
assert pp.magnification == 0.5, msg
else:
assert pp.magnification is None, msg
assert pp.x == [-150, -300, -300, 0, 700, 700, 700][ii], msg
assert pp.y == [200, 800, 1200, 1200, 400, 1400, 2400][ii], msg
assert pp.angle == [0, None, None, None, None, 90, -90][ii], msg
placements2 = layout.cells[0].placements
assert len(placements2) == 2
for ii, pp in enumerate(placements2):
msg = f'Failed on placement {ii} in cell TOPTOP'
assert not pp.properties, msg
assert pp.name.string == 'TOP', msg
assert not pp.flip, msg
assert pp.repetition is None, msg
assert pp.angle == [90, None][ii], msg
assert pp.magnification == [0.5, None][ii], msg
assert pp.x == [100, 200][ii], msg
assert pp.y == [0, 1000][ii], msg
def write_file_8(buf: IO[bytes]) -> IO[bytes]:
"""
"""
buf.write(HEADER)
write_uint(buf, 14) # CELL record (explicit)
write_bstring(buf, b'TOPTOP') # Cell name
write_uint(buf, 15) # XYABSOLUTE record
write_uint(buf, 18) # PLACEMENT (mag 0.5, arbitrary angle)
write_byte(buf, 0b1011_0110) # CNXY_RMAF
write_bstring(buf, b'TOP') # Cell reference
write_uint(buf, 6) # magnitude, float32
write_float32(buf, 0.5) # (magnitude)
write_uint(buf, 7) # angle, float64
write_float64(buf, 22.5) # (angle)
write_sint(buf, 100) # placement-x (absolute)
write_sint(buf, 0) # placement-y (absolute)
write_uint(buf, 18) # PLACEMENT (mag 1.0, manhattan)
write_byte(buf, 0b1011_0110) # CNXY_RMAF
write_bstring(buf, b'TOP') # Cell reference
write_uint(buf, 6) # magnitude, float32
write_float32(buf, 1.0) # (magnitude)
write_uint(buf, 7) # angle, float64
write_float64(buf, 0.0) # (angle)
write_sint(buf, 1100) # placement-x (absolute)
write_sint(buf, 0) # placement-y (absolute)
write_uint(buf, 14) # CELL record (explicit)
write_bstring(buf, b'TOP') # Cell name
write_uint(buf, 18) # PLACEMENT (mag 2.0, manhattan)
write_byte(buf, 0b1011_0110) # CNXY_RMAF
write_bstring(buf, b'A') # Cell reference
write_uint(buf, 6) # magnitude, float32
write_float32(buf, 2.0) # (magnitude)
write_uint(buf, 7) # angle, float64
write_float64(buf, 0.0) # (angle)
write_sint(buf, -100) # placement-x (absolute)
write_sint(buf, 100) # placement-y (absolute)
write_uint(buf, 18) # PLACEMENT (mag 1.0, arbitrary angle)
write_byte(buf, 0b1011_0110) # CNXY_RMAF
write_bstring(buf, b'A') # Cell reference
write_uint(buf, 6) # magnitude, float32
write_float32(buf, 1.0) # (magnitude)
write_uint(buf, 7) # angle, float64
write_float64(buf, 45.0) # (angle)
write_sint(buf, -150) # placement-x (absolute)
write_sint(buf, 1100) # placement-y (absolute)
write_uint(buf, 18) # PLACEMENT (mag 0.5, arbitrary angle)
write_byte(buf, 0b1011_1111) # CNXY_RMAF
write_bstring(buf, b'A') # Cell reference
write_uint(buf, 6) # magnitude, float32
write_float32(buf, 0.5) # (magnitude)
write_uint(buf, 7) # angle, float64
write_float64(buf, 135.0) # (angle)
write_sint(buf, -200) # placement-x (absolute)
write_sint(buf, 2100) # placement-y (absolute)
write_uint(buf, 1) # repetition (3x4 matrix)
write_uint(buf, 1) # (repetition) x-dimension
write_uint(buf, 2) # (repetition) y-dimension
write_uint(buf, 200) # (repetition) x-spacing
write_uint(buf, 300) # (repetition) y-spacing
write_uint(buf, 14) # CELL record (explicit)
write_bstring(buf, b'A') # Cell name
write_rectangle(buf, pos=(30, -40))
buf.write(FOOTER)
return buf
def test_file_8() -> None:
buf = write_file_8(BytesIO())
buf.seek(0)
layout = OasisLayout.read(buf)
base_tests(layout)
assert len(layout.cells) == 3
assert layout.cells[0].name.string == 'TOPTOP'
assert not layout.cells[0].properties
assert not layout.cells[0].geometry
assert layout.cells[1].name.string == 'TOP'
assert not layout.cells[1].properties
assert not layout.cells[1].geometry
assert layout.cells[2].name.string == 'A'
assert not layout.cells[2].properties
assert not layout.cells[2].placements
geometry = cast(list[Rectangle], layout.cells[2].geometry)
assert len(geometry) == 1
assert geometry[0].layer == 1
assert geometry[0].datatype == 2
assert geometry[0].width == 100
assert geometry[0].height == 200
assert geometry[0].x == 30
assert geometry[0].y == -40
placements = layout.cells[1].placements
assert len(placements) == 3
for ii, pp in enumerate(placements):
msg = f'Failed on placement {ii} in cell TOP'
assert not pp.properties, msg
assert pp.name.string == 'A', msg
assert pp.flip == (ii == 2), msg
assert pp.magnification == [2, 1, 0.5][ii], msg
assert pp.angle == [0, 45, 135][ii], msg
assert pp.x == [-100, -150, -200][ii], msg
assert pp.y == [100, 1100, 2100][ii], msg
if ii < 2:
assert pp.repetition is None, msg
assert placements[2].repetition.a_count == 3
assert placements[2].repetition.b_count == 4
assert placements[2].repetition.a_vector == [200, 0]
assert placements[2].repetition.b_vector == [0, 300]
placements2 = layout.cells[0].placements
assert len(placements2) == 2
for ii, pp in enumerate(placements2):
msg = f'Failed on placement {ii} in cell TOPTOP'
assert not pp.properties, msg
assert pp.name.string == 'TOP', msg
assert not pp.flip, msg
assert pp.repetition is None, msg
assert pp.angle == [22.5, 0][ii], msg
assert pp.magnification == [0.5, 1.0][ii], msg
assert pp.x == [100, 1100][ii], msg
assert pp.y == [0, 0][ii], msg

View File

@ -1,451 +0,0 @@
# mypy: disable-error-code="union-attr, arg-type"
from typing import IO
from io import BytesIO
import numpy
from numpy.testing import assert_equal
from .utils import HEADER, FOOTER
from ..basic import write_uint, write_sint, write_bstring, write_byte
from ..main import OasisLayout
def base_tests(layout: OasisLayout) -> None:
assert layout.version.string == '1.0'
assert layout.unit == 1000
assert layout.validation.checksum_type == 0
assert not layout.properties
assert not layout.xnames
assert not layout.textstrings
assert not layout.cellnames
assert not layout.layers
assert len(layout.cells) == 1
assert layout.cells[0].name.string == 'ABC'
assert not layout.cells[0].properties
def common_tests(layout: OasisLayout) -> None:
geometry = layout.cells[0].geometry
assert len(geometry) == 12
assert geometry[0].x == 0
assert geometry[0].y == 100
assert geometry[1].x == -200
assert geometry[1].y == 400
assert geometry[2].x == 0
assert geometry[2].y == 400
assert geometry[3].x == 0
assert geometry[3].y == 1000
assert geometry[4].x == 200
assert geometry[4].y == 1000
assert geometry[5].x == 400
assert geometry[5].y == 1000
assert geometry[6].x == 700
assert geometry[6].y == 1000
assert geometry[7].x == 900
assert geometry[7].y == 1000
assert geometry[8].x == 1100
assert geometry[8].y == 1000
assert geometry[9].x == 0
assert geometry[9].y == 2000
assert geometry[10].x == 1000
assert geometry[10].y == 2000
assert geometry[11].x == 2000
assert geometry[11].y == 2000
for ii, gg in enumerate(geometry):
msg = f'Failed on polygon {ii}'
if ii < 2:
assert gg.layer == 1, msg
assert gg.datatype == 2, msg
elif ii < 10:
assert gg.layer == 2, msg
assert gg.datatype == 3, msg
else:
assert gg.layer == 2, msg
assert gg.datatype == 1, msg
if ii < 9:
assert gg.repetition is None, msg
elif ii in (9, 10):
assert gg.repetition.a_count == 3, msg
assert gg.repetition.b_count == 4, msg
assert gg.repetition.a_vector == [200, 0], msg
assert gg.repetition.b_vector == [0, 300], msg
assert geometry[11].repetition.y_displacements == [200, 300]
for ii in range(4):
msg = f'Fail on poly {ii}'
assert len(geometry[0].point_list) == 6, msg
assert_equal(
geometry[0].point_list,
[[150, 0], [0, 50], [-50, 0], [0, 50], [-100, 0], [0, -100]],
err_msg=msg,
)
assert len(geometry[4].point_list) == 6
assert_equal(geometry[4].point_list, [[0, 150], [50, 0], [0, -50], [50, 0], [0, -100], [-100, 0]])
assert len(geometry[5].point_list) == 8
assert_equal(geometry[5].point_list, [[150, 0], [0, 50], [-50, 0], [0, 50], [-50, 0], [0, -50], [-50, 0], [0, -50]])
assert len(geometry[6].point_list) == 9
assert_equal(geometry[6].point_list, [[25, 0], [50, 50], [0, 50], [-50, 50], [-50, 0], [-50, -50], [0, -50], [50, -50], [25, 0]])
assert len(geometry[7].point_list) == 9
assert_equal(geometry[7].point_list, [[25, 0], [50, 50], [0, 50], [-50, 50], [-50, 0], [-50, -50], [10, -75], [25, -25], [40, 0]])
assert len(geometry[8].point_list) == 9
assert_equal(
geometry[8].point_list,
numpy.cumsum([[25, 0], [50, 50], [0, 50], [-50, 50], [-50, 0], [-50, -50], [10, -75], [25, -25], [45, -575]], axis=0),
)
for ii in range(9, 12):
msg = f'Fail on poly {ii}'
assert len(geometry[ii].point_list) == 6, msg
assert_equal(geometry[ii].point_list, [[0, 150], [50, 0], [0, -50], [50, 0], [0, -100], [-100, 0]], err_msg=msg)
def write_file_common(buf: IO[bytes], variant: int) -> IO[bytes]:
"""
"""
assert variant in (1, 3), 'Error in test!!'
buf.write(HEADER)
if variant == 3:
write_uint(buf, 7) # PROPNAME record (implict id 0)
write_bstring(buf, b'PROP0') # property name
write_uint(buf, 14) # CELL record (explicit)
write_bstring(buf, b'ABC') # Cell name
# POLYGON 0
write_uint(buf, 21) # POLYGON record
write_byte(buf, 0b0011_1011) # 00PX_YRDL
write_uint(buf, 1) # layer
write_uint(buf, 2) # datatype
write_uint(buf, 0) # pointlist: 1-delta, horiz-fisrt
write_uint(buf, 4) # (pointlist) dimension
write_sint(buf, 150) # (pointlist)
write_sint(buf, 50) # (pointlist)
write_sint(buf, -50) # (pointlist)
write_sint(buf, 50) # (pointlist)
write_sint(buf, 0) # geometry-x (absolute)
write_sint(buf, 100) # geometry-y (absolute)
if variant == 3:
# PROPERTY 0
write_uint(buf, 28) # PROPERTY record (explicit)
write_byte(buf, 0b0001_0110) # UUUU_VCNS
write_uint(buf, 0) # propname id
write_uint(buf, 2) # property value (real: positive reciprocal)
write_uint(buf, 5) # (real) 1/5
write_uint(buf, 16) # XYRELATIVE record
# Polygon 1
write_uint(buf, 21) # POLYGON record
write_byte(buf, 0b0011_1011) # 00PX_YRDL
write_uint(buf, 1) # layer
write_uint(buf, 2) # datatype
write_uint(buf, 0) # pointlist: 1-delta, horiz-fisrt
write_uint(buf, 4) # (pointlist) dimension
write_sint(buf, 150) # (pointlist)
write_sint(buf, 50) # (pointlist)
write_sint(buf, -50) # (pointlist)
write_sint(buf, 50) # (pointlist)
write_sint(buf, -200) # geometry-x (relative)
write_sint(buf, 300) # geometry-y (relative)
if variant == 3:
# PROPERTY 1
write_uint(buf, 29) # PROPERTY record (repeat)
write_uint(buf, 15) # XYABSOLUTE record
# Polygon 2
write_uint(buf, 21) # POLYGON record
write_byte(buf, 0b0011_0011) # 00PX_YRDL
write_uint(buf, 2) # layer
write_uint(buf, 3) # datatype
write_uint(buf, 0) # pointlist: 1-delta, horiz-fisrt
write_uint(buf, 4) # (pointlist) dimension
write_sint(buf, 150) # (pointlist)
write_sint(buf, 50) # (pointlist)
write_sint(buf, -50) # (pointlist)
write_sint(buf, 50) # (pointlist)
write_sint(buf, 0) # geometry-x (absolute)
if variant == 3:
# PROPERTY 2
write_uint(buf, 29) # PROPERTY record (repeat)
# Polygon 3
write_uint(buf, 21) # POLYGON record
write_byte(buf, 0b0000_1000) # 00PX_YRDL
write_sint(buf, 1000) # geometry-y (absolute)
if variant == 3:
# PROPERTY 3
write_uint(buf, 29) # PROPERTY record (repeat)
# Polygon 4
write_uint(buf, 21) # POLYGON record
write_byte(buf, 0b0011_0011) # 00PX_YRDL
write_uint(buf, 2) # layer
write_uint(buf, 3) # datatype
write_uint(buf, 1) # pointlist: 1-delta, vert-fisrt
write_uint(buf, 4) # (pointlist) dimension
write_sint(buf, 150) # (pointlist)
write_sint(buf, 50) # (pointlist)
write_sint(buf, -50) # (pointlist)
write_sint(buf, 50) # (pointlist)
write_sint(buf, 200) # geometry-x (absolute)
if variant == 3:
# PROPERTY 4
write_uint(buf, 29) # PROPERTY record (repeat)
# Polygon 5
write_uint(buf, 21) # POLYGON record
write_byte(buf, 0b0011_0011) # 00PX_YRDL
write_uint(buf, 2) # layer
write_uint(buf, 3) # datatype
write_uint(buf, 2) # pointlist: 2-delta
write_uint(buf, 7) # (pointlist) dimension
write_uint(buf, 150 << 2 | 0b00) # (pointlist)
write_uint(buf, 50 << 2 | 0b01) # (pointlist)
write_uint(buf, 50 << 2 | 0b10) # (pointlist)
write_uint(buf, 50 << 2 | 0b01) # (pointlist)
write_uint(buf, 50 << 2 | 0b10) # (pointlist)
write_uint(buf, 50 << 2 | 0b11) # (pointlist)
write_uint(buf, 50 << 2 | 0b10) # (pointlist)
write_sint(buf, 400) # geometry-x (absolute)
if variant == 3:
# PROPERTY 5
write_uint(buf, 29) # PROPERTY record (repeat)
# Polygon 6
write_uint(buf, 21) # POLYGON record
write_byte(buf, 0b0011_0011) # 00PX_YRDL
write_uint(buf, 2) # layer
write_uint(buf, 3) # datatype
write_uint(buf, 3) # pointlist: 3-delta
write_uint(buf, 8) # (pointlist) dimension
write_uint(buf, 25 << 3 | 0b000) # (pointlist)
write_uint(buf, 50 << 3 | 0b100) # (pointlist)
write_uint(buf, 50 << 3 | 0b001) # (pointlist)
write_uint(buf, 50 << 3 | 0b101) # (pointlist)
write_uint(buf, 50 << 3 | 0b010) # (pointlist)
write_uint(buf, 50 << 3 | 0b110) # (pointlist)
write_uint(buf, 50 << 3 | 0b011) # (pointlist)
write_uint(buf, 50 << 3 | 0b111) # (pointlist)
write_sint(buf, 700) # geometry-x (absolute)
if variant == 3:
# PROPERTY 6
write_uint(buf, 29) # PROPERTY record (repeat)
# Polygon 7
write_uint(buf, 21) # POLYGON record
write_byte(buf, 0b0011_0011) # 00PX_YRDL
write_uint(buf, 2) # layer
write_uint(buf, 3) # datatype
write_uint(buf, 4) # pointlist: g-delta
write_uint(buf, 8) # (pointlist) dimension
write_uint(buf, 25 << 4 | 0b0000) # (pointlist)
write_uint(buf, 50 << 4 | 0b1000) # (pointlist)
write_uint(buf, 50 << 4 | 0b0010) # (pointlist)
write_uint(buf, 50 << 2 | 0b11) # (pointlist)
write_sint(buf, 50)
write_uint(buf, 50 << 4 | 0b0100) # (pointlist)
write_uint(buf, 50 << 4 | 0b1100) # (pointlist)
write_uint(buf, 10 << 2 | 0b01) # (pointlist)
write_sint(buf, -75)
write_uint(buf, 25 << 4 | 0b1110) # (pointlist)
write_sint(buf, 900) # geometry-x (absolute)
if variant == 3:
# PROPERTY 7
write_uint(buf, 29) # PROPERTY record (repeat)
# Polygon 8
write_uint(buf, 21) # POLYGON record
write_byte(buf, 0b0011_0011) # 00PX_YRDL
write_uint(buf, 2) # layer
write_uint(buf, 3) # datatype
write_uint(buf, 5) # pointlist: double g-delta
write_uint(buf, 8) # (pointlist) dimension
write_uint(buf, 25 << 4 | 0b0000) # (pointlist)
write_uint(buf, 50 << 4 | 0b1000) # (pointlist)
write_uint(buf, 50 << 4 | 0b0010) # (pointlist)
write_uint(buf, 50 << 2 | 0b11) # (pointlist)
write_sint(buf, 50)
write_uint(buf, 50 << 4 | 0b0100) # (pointlist)
write_uint(buf, 50 << 4 | 0b1100) # (pointlist)
write_uint(buf, 10 << 2 | 0b01) # (pointlist)
write_sint(buf, -75)
write_uint(buf, 25 << 4 | 0b1110) # (pointlist)
write_sint(buf, 1100) # geometry-x (absolute)
if variant == 3:
# PROPERTY 8
write_uint(buf, 29) # PROPERTY record (repeat)
# Polygon 9
write_uint(buf, 21) # POLYGON record
write_byte(buf, 0b0011_1111) # 00PX_YRDL
write_uint(buf, 2) # layer
write_uint(buf, 3) # datatype
write_uint(buf, 1) # pointlist: 1-delta (vert. first)
write_uint(buf, 4) # (pointlist) dimension
write_sint(buf, 150) # (pointlist)
write_sint(buf, 50) # (pointlist)
write_sint(buf, -50) # (pointlist)
write_sint(buf, 50) # (pointlist)
write_sint(buf, 0) # geometry-x (absolute)
write_sint(buf, 2000) # geometry-y (absolute)
write_uint(buf, 1) # repetition (3x4 matrix)
write_uint(buf, 1) # (repetition) x-dimension
write_uint(buf, 2) # (repetition) y-dimension
write_uint(buf, 200) # (repetition) x-spacing
write_uint(buf, 300) # (repetition) y-spacing
if variant == 3:
# PROPERTY 9
write_uint(buf, 29) # PROPERTY record (repeat)
write_uint(buf, 16) # XYRELATIVE record
# Polygon 10
write_uint(buf, 21) # POLYGON record
write_byte(buf, 0b0011_0110) # 00PX_YRDL
write_uint(buf, 1) # datatype
write_uint(buf, 1) # pointlist: 1-delta (vert. first)
write_uint(buf, 4) # (pointlist) dimension
write_sint(buf, 150) # (pointlist)
write_sint(buf, 50) # (pointlist)
write_sint(buf, -50) # (pointlist)
write_sint(buf, 50) # (pointlist)
write_sint(buf, 1000) # geometry-x (relative)
write_uint(buf, 0) # repetition (reuse)
if variant == 3:
# PROPERTY 10
write_uint(buf, 29) # PROPERTY record (repeat)
# Polygon 11
write_uint(buf, 21) # POLYGON record
write_byte(buf, 0b0011_0110) # 00PX_YRDL
write_uint(buf, 1) # datatype
write_uint(buf, 1) # pointlist: 1-delta (vert. first)
write_uint(buf, 4) # (pointlist) dimension
write_sint(buf, 150) # (pointlist)
write_sint(buf, 50) # (pointlist)
write_sint(buf, -50) # (pointlist)
write_sint(buf, 50) # (pointlist)
write_sint(buf, 1000) # geometry-x (relative)
write_uint(buf, 6) # repetition (3 rows)
write_uint(buf, 1) # (repetition) dimension
write_uint(buf, 200) # (repetition) y-delta
write_uint(buf, 300) # (repetition) y-delta
if variant == 3:
# PROPERTY 11
write_uint(buf, 29) # PROPERTY record (repeat)
buf.write(FOOTER)
return buf
def test_file_1() -> None:
buf = write_file_common(BytesIO(), 1)
buf.seek(0)
layout = OasisLayout.read(buf)
base_tests(layout)
common_tests(layout)
assert not layout.propnames
geometry = layout.cells[0].geometry
for ii, gg in enumerate(geometry):
assert not gg.properties, f'Fail on polygon {ii}'
def write_file_2(buf: IO[bytes]) -> IO[bytes]:
"""
"""
buf.write(HEADER)
write_uint(buf, 14) # CELL record (explicit)
write_bstring(buf, b'ABC') # Cell name
write_uint(buf, 15) # XYRELATIVE record
# POLYGON 0
write_uint(buf, 21) # POLYGON record
write_byte(buf, 0b0011_0011) # 00PX_YRDL
write_uint(buf, 2) # layer
write_uint(buf, 3) # datatype
write_uint(buf, 4) # pointlist: g-delta
write_uint(buf, 8002) # (pointlist) dimension
write_uint(buf, 1000 << 2 | 0b11) # (pointlist)
write_sint(buf, 0) # (pointlist)
for _ in range(4000):
write_uint(buf, 10 << 2 | 0b01) # (pointlist)
write_sint(buf, 20) # (pointlist)
write_uint(buf, 10 << 2 | 0b11) # (pointlist)
write_sint(buf, 20) # (pointlist)
write_uint(buf, 1000 << 2 | 0b01) # (pointlist)
write_sint(buf, 0) # (pointlist)
write_sint(buf, 0) # geometry-x (absolute)
buf.write(FOOTER)
return buf
def test_file_2() -> None:
buf = write_file_2(BytesIO())
buf.seek(0)
layout = OasisLayout.read(buf)
base_tests(layout)
assert len(layout.cells[0].geometry) == 1
poly = layout.cells[0].geometry[0]
assert poly.layer == 2
assert poly.datatype == 3
assert poly.x == 0
assert poly.y == 0
assert len(poly.point_list) == 8002 + 1
assert_equal(poly.point_list,
([[-1000, 0]]
+ [[(-1) ** nn * 10, 20] for nn in range(8000)]
+ [[1000, 0], [0, -20 * 8000]]),
)
def test_file_3() -> None:
buf = write_file_common(BytesIO(), 3)
buf.seek(0)
layout = OasisLayout.read(buf)
base_tests(layout)
common_tests(layout)
assert len(layout.propnames) == 1
assert layout.propnames[0].string == 'PROP0'
geometry = layout.cells[0].geometry
for ii, gg in enumerate(geometry):
msg = f'Fail on polygon {ii}'
assert len(gg.properties) == 1, msg
assert gg.properties[0].name == 0, msg # type: ignore
assert len(gg.properties[0].values) == 1, msg
assert gg.properties[0].values[0] * 5 == 1, msg # type: ignore

File diff suppressed because it is too large Load Diff

View File

@ -1,273 +0,0 @@
# mypy: disable-error-code="union-attr"
from typing import IO
from io import BytesIO
from .utils import HEADER, FOOTER
from ..basic import write_uint, write_sint, write_bstring, write_byte
from ..main import OasisLayout
def base_tests(layout: OasisLayout) -> None:
assert layout.version.string == '1.0'
assert layout.unit == 1000
assert layout.validation.checksum_type == 0
assert not layout.properties
assert not layout.xnames
assert not layout.textstrings
assert not layout.cellnames
assert not layout.layers
assert len(layout.cells) == 1
assert layout.cells[0].name.string == 'ABC'
assert not layout.cells[0].properties
geometry = layout.cells[0].geometry
assert geometry[0].x == 300
assert geometry[0].y == -400
assert geometry[1].x == 400
assert geometry[1].y == -500
assert geometry[2].x == 600
assert geometry[2].y == -300
assert geometry[3].x == 800
assert geometry[3].y == -300
assert geometry[4].y == -600
assert geometry[5].y == -900
assert geometry[6].y == -1200
assert geometry[7].y == -1500
assert geometry[8].y == -1800
assert geometry[9].y == 500
assert geometry[10].y == 2000
for ii, gg in enumerate(geometry[3:]):
assert gg.x == 800, f'Failed on rectangle {ii + 3}'
for ii, gg in enumerate(geometry):
msg = f'Failed on rectangle {ii}'
if ii < 4:
assert gg.layer == 1, msg
assert gg.datatype == 2, msg
else:
assert gg.layer == 2, msg
assert gg.datatype == 3, msg
if ii < 7:
assert gg.width == 100, msg
assert gg.height == 200, msg
elif ii == 7:
assert gg.width == 150, msg
assert gg.height is None, msg
else:
assert gg.width == 150, msg
assert gg.height == 150, msg
if ii < 9:
assert gg.repetition is None, msg
assert geometry[9].repetition.a_count == 3
assert geometry[9].repetition.b_count == 4
assert geometry[9].repetition.a_vector == [200, 0]
assert geometry[9].repetition.b_vector == [0, 300]
assert geometry[10].repetition.x_displacements == [200, 300]
def write_file_common(buf: IO[bytes], variant: int) -> IO[bytes]:
"""
"""
assert variant in (1, 2), 'Error in test!!'
buf.write(HEADER)
if variant == 2:
write_uint(buf, 7) # PROPNAME record (implict id 0)
write_bstring(buf, b'PROP0') # property name
write_uint(buf, 14) # CELL record (explicit)
write_bstring(buf, b'ABC') # Cell name
# RECTANGLE 0
write_uint(buf, 20) # RECTANGLE record
write_byte(buf, 0b0111_1011) # SWHX_YRDL
write_uint(buf, 1) # layer
write_uint(buf, 2) # datatype
write_uint(buf, 100) # width
write_uint(buf, 200) # height
write_sint(buf, 300) # geometry-x (absolute)
write_sint(buf, -400) # geometry-y (absolute)
if variant == 2:
# PROPERTY 0
write_uint(buf, 28) # PROPERTY record (explicit)
write_byte(buf, 0b0001_0110) # UUUU_VCNS
write_uint(buf, 0) # propname id
write_uint(buf, 2) # property value (real: positive reciprocal)
write_uint(buf, 5) # (real) 1/5
write_uint(buf, 16) # XYRELATIVE record
# RECTANGLE 1
write_uint(buf, 20) # RECTANGLE record
write_byte(buf, 0b0111_1011) # SWHX_YRDL
write_uint(buf, 1) # layer
write_uint(buf, 2) # datatype
write_uint(buf, 100) # width
write_uint(buf, 200) # height
write_sint(buf, 100) # geometry-x (relative)
write_sint(buf, -100) # geometry-y (relative)
if variant == 2:
# PROPERTY 1
write_uint(buf, 29) # PROPERTY record (repeat)
write_uint(buf, 15) # XYABSOLUTE record
# RECTANGLE 2
write_uint(buf, 20) # RECTANGLE record
write_byte(buf, 0b0111_1011) # SWHX_YRDL
write_uint(buf, 1) # layer
write_uint(buf, 2) # datatype
write_uint(buf, 100) # width
write_uint(buf, 200) # height
write_sint(buf, 600) # geometry-x (absolute)
write_sint(buf, -300) # geometry-y (absolute)
if variant == 2:
# PROPERTY 2
write_uint(buf, 29) # PROPERTY record (repeat)
# RECTANGLE 3
write_uint(buf, 20) # RECTANGLE record
write_byte(buf, 0b0111_0011) # SWHX_YRDL
write_uint(buf, 1) # layer
write_uint(buf, 2) # datatype
write_uint(buf, 100) # width
write_uint(buf, 200) # height
write_sint(buf, 800) # geometry-x (absolute)
if variant == 2:
# PROPERTY 3
write_uint(buf, 29) # PROPERTY record (repeat)
# RECTANGLE 4
write_uint(buf, 20) # RECTANGLE record
write_byte(buf, 0b0110_1011) # SWHX_YRDL
write_uint(buf, 2) # layer
write_uint(buf, 3) # datatype
write_uint(buf, 100) # width
write_uint(buf, 200) # height
write_sint(buf, -600) # geometry-y (absolute)
if variant == 2:
# PROPERTY 4
write_uint(buf, 29) # PROPERTY record (repeat)
# RECTANGLE 5
write_uint(buf, 20) # RECTANGLE record
write_byte(buf, 0b0110_1000) # SWHX_YRDL
write_uint(buf, 100) # width
write_uint(buf, 200) # height
write_sint(buf, -900) # geometry-y (absolute)
if variant == 2:
# PROPERTY 5
write_uint(buf, 29) # PROPERTY record (repeat)
# RECTANGLE 6
write_uint(buf, 20) # RECTANGLE record
write_byte(buf, 0b0000_1000) # SWHX_YRDL
write_sint(buf, -1200) # geometry-y (absolute)
if variant == 2:
# PROPERTY 6
write_uint(buf, 29) # PROPERTY record (repeat)
# RECTANGLE 7
write_uint(buf, 20) # RECTANGLE record
write_byte(buf, 0b1100_1000) # SWHX_YRDL
write_uint(buf, 150) # width
write_sint(buf, -1500) # geometry-y (absolute)
if variant == 2:
# PROPERTY 7
write_uint(buf, 29) # PROPERTY record (repeat)
# RECTANGLE 8
write_uint(buf, 20) # RECTANGLE record
write_byte(buf, 0b0000_1000) # SWHX_YRDL
write_sint(buf, -1800) # geometry-y (absolute)
if variant == 2:
# PROPERTY 8
write_uint(buf, 29) # PROPERTY record (repeat)
# RECTANGLE 9
write_uint(buf, 20) # RECTANGLE record
write_byte(buf, 0b0000_1100) # SWHX_YRDL
write_sint(buf, 500) # geometry-y (absolute)
write_uint(buf, 1) # repetition (3x4 matrix)
write_uint(buf, 1) # (repetition) x-dimension
write_uint(buf, 2) # (repetition) y-dimension
write_uint(buf, 200) # (repetition) x-spacing
write_uint(buf, 300) # (repetition) y-spacing
if variant == 2:
# PROPERTY 9
write_uint(buf, 29) # PROPERTY record (repeat)
# RECTANGLE 10
write_uint(buf, 20) # RECTANGLE record
write_byte(buf, 0b0000_1100) # SWHX_YRDL
write_sint(buf, 2000) # geometry-y (absolute)
write_uint(buf, 4) # repetition (3 arbitrary cols.)
write_uint(buf, 1) # (repetition) dimension
write_uint(buf, 200) # (repetition) x-delta
write_uint(buf, 300) # (repetition) x-delta
if variant == 2:
# PROPERTY 10
write_uint(buf, 29) # PROPERTY record (repeat)
buf.write(FOOTER)
return buf
def test_file_1() -> None:
buf = write_file_common(BytesIO(), 1)
buf.seek(0)
layout = OasisLayout.read(buf)
base_tests(layout)
assert not layout.propnames
geometry = layout.cells[0].geometry
for ii, gg in enumerate(geometry):
assert not gg.properties, f'Fail on rectangle {ii}'
def test_file_2() -> None:
buf = write_file_common(BytesIO(), 2)
buf.seek(0)
layout = OasisLayout.read(buf)
base_tests(layout)
assert len(layout.propnames) == 1
assert layout.propnames[0].string == 'PROP0'
geometry = layout.cells[0].geometry
for ii, gg in enumerate(geometry):
msg = f'Failed on rectangle {ii}'
assert len(gg.properties) == 1, msg
prop = gg.properties[0]
assert prop.name == 0, msg
assert len(prop.values) == 1, msg # type: ignore
assert prop.values[0].numerator == 1, msg # type: ignore
assert prop.values[0].denominator == 5, msg # type: ignore

View File

@ -1,735 +0,0 @@
# mypy: disable-error-code="union-attr, index"
from typing import IO
from io import BytesIO
import pytest
from .utils import HEADER, FOOTER
from ..basic import write_uint, write_sint, write_bstring, write_byte
from ..basic import InvalidRecordError, InvalidDataError
from ..basic import GridRepetition, ArbitraryRepetition
from ..main import OasisLayout
def base_tests(layout: OasisLayout) -> None:
assert layout.version.string == '1.0'
assert layout.unit == 1000
assert layout.validation.checksum_type == 0
assert not layout.properties
assert not layout.propnames
assert not layout.xnames
assert not layout.cellnames
assert not layout.propstrings
assert not layout.layers
def common_tests(layout: OasisLayout) -> None:
assert len(layout.cells) == 1
assert layout.cells[0].name.string == 'ABC'
geometry = layout.cells[0].geometry
assert geometry[0].layer == 1
assert geometry[0].datatype == 2
for ii, gg in enumerate(geometry[1:]):
assert gg.layer == 2, f'textstring #{ii + 1}'
assert gg.datatype == 1, f'textstring #{ii + 1}'
assert geometry[0].x == 100
assert geometry[0].y == -200
assert geometry[1].x == 200
assert geometry[1].y == -400
assert geometry[2].y == -400
for ii, gg in enumerate(geometry[2:]):
assert gg.x == 300, f'textstring #{ii + 2}'
for ii, gg in enumerate(geometry[3:]):
assert gg.y == -300 - 200 * ii, f'textstring #{ii + 3}'
for ii, gg in enumerate(geometry):
if ii < 4:
assert gg.repetition is None, f'textstring #{ii}'
elif ii in (4, 5, 6, 7, 12, 13, 14, 15):
assert isinstance(gg.repetition, GridRepetition), f'textstring #{ii}'
else:
assert isinstance(gg.repetition, ArbitraryRepetition), f'textstring #{ii}'
for ii in (4, 5):
assert geometry[ii].repetition.a_count == 3, f'textstring #{ii}'
assert geometry[ii].repetition.b_count == 4, f'textstring #{ii}'
assert geometry[ii].repetition.a_vector == [10, 0], f'textstring #{ii}'
assert geometry[ii].repetition.b_vector == [0, 12], f'textstring #{ii}'
assert geometry[6].repetition.a_count == 3
assert geometry[6].repetition.a_vector == [10, 0]
assert geometry[7].repetition.a_count == 4
assert geometry[7].repetition.a_vector == [0, 12]
assert geometry[8].repetition.x_displacements == [12, 13, 14]
assert geometry[9].repetition.x_displacements == [4 * 3, 5 * 3, 6 * 3]
assert geometry[10].repetition.y_displacements == [10, 11]
assert geometry[11].repetition.y_displacements == [2 * 5, 3 * 5]
assert geometry[12].repetition.a_count == 3
assert geometry[12].repetition.b_count == 4
assert geometry[12].repetition.a_vector == [10, 0]
assert geometry[12].repetition.b_vector == [-11, -12]
assert geometry[13].repetition.a_count == 3
assert geometry[13].repetition.b_count == 4
assert geometry[13].repetition.a_vector == [11, 12]
assert geometry[13].repetition.b_vector == [-10, 10]
assert geometry[14].repetition.a_count == 3
assert geometry[14].repetition.b_count is None
assert geometry[14].repetition.a_vector == [11, 12]
assert geometry[14].repetition.b_vector is None
assert geometry[15].repetition.a_count == 4
assert geometry[15].repetition.b_count is None
assert geometry[15].repetition.a_vector == [-10, 10]
assert geometry[15].repetition.b_vector is None
assert geometry[17].repetition.x_displacements == [-11, 10]
assert geometry[17].repetition.y_displacements == [12, -10]
assert geometry[19].repetition.x_displacements == [-12, 9]
assert geometry[19].repetition.y_displacements == [12, -9]
def write_file_common(buf: IO[bytes], variant: int) -> IO[bytes]:
"""
Single cell with explicit name 'XYZ'
"""
assert variant in (1, 2, 5, 12), 'Error in test!!'
buf.write(HEADER)
if variant == 2:
write_uint(buf, 6) # TEXTSTRING record (explicit id)
write_bstring(buf, b'A')
write_uint(buf, 1) # id
write_uint(buf, 6) # TEXTSTRING record (explicit id)
write_bstring(buf, b'B')
write_uint(buf, 2) # id
elif variant == 5:
write_uint(buf, 5) # TEXTSTRING record (implicit id 0)
write_bstring(buf, b'A')
write_uint(buf, 5) # TEXTSTRING record (implicit id 1)
write_bstring(buf, b'B')
write_uint(buf, 14) # CELL record (explicit)
write_bstring(buf, b'ABC') # Cell name
# TEXT 0
write_uint(buf, 19) # TEXT record
if variant == 1:
write_byte(buf, 0b0101_1011) # 0CNX_YRTL
write_bstring(buf, b'TEXT_ABC') # text string
elif variant in (2, 5, 12):
write_byte(buf, 0b0111_1011) # 0CNX_YRTL
write_uint(buf, 1) # textstring id
write_uint(buf, 1) # layer
write_uint(buf, 2) # datatype
write_sint(buf, 100) # x
write_sint(buf, -200) # y
write_uint(buf, 16) # XYRELATIVE
# TEXT 1
write_uint(buf, 19) # TEXT record
if variant == 1:
write_byte(buf, 0b0101_1011) # 0CNX_YRTL
write_bstring(buf, b'TEXT_ABC') # text string
elif variant in (2, 12):
write_byte(buf, 0b0111_1011) # 0CNX_YRTL
write_uint(buf, 2) # textstring id
elif variant == 5:
write_byte(buf, 0b0111_1011) # 0CNX_YRTL
write_uint(buf, 0) # textstring id
write_uint(buf, 2) # layer
write_uint(buf, 1) # datatype
write_sint(buf, 100) # x
write_sint(buf, -200) # y
write_uint(buf, 15) # XYABSOLUTE
# TEXT 2
write_uint(buf, 19) # TEXT record
write_byte(buf, 0b0001_0000) # 0CNX_YRTL
write_sint(buf, 300) # x
# TEXT 3
write_uint(buf, 19) # TEXT record
write_byte(buf, 0b0000_1000) # 0CNX_YRTL
write_sint(buf, -300) # y
write_uint(buf, 16) # XYRELATIVE
# TEXT 4
write_uint(buf, 19) # TEXT record
if variant == 1:
write_byte(buf, 0b0000_1100) # 0CNX_YRTL
elif variant in (2, 5, 12):
write_byte(buf, 0b0110_1100) # 0CNX_YRTL
write_uint(buf, 1) # textstring id
write_sint(buf, -200) # y
write_uint(buf, 1) # repetition (3x4)
write_uint(buf, 1) # (repetition)
write_uint(buf, 2) # (repetition)
write_uint(buf, 10) # (repetition)
write_uint(buf, 12) # (repetition)
# TEXT 5
write_uint(buf, 19) # TEXT record
write_byte(buf, 0b0000_1100) # 0CNX_YRTL
write_sint(buf, -200) # y
write_uint(buf, 0) # repetition (reuse)
# TEXT 6
write_uint(buf, 19) # TEXT record
write_byte(buf, 0b0000_1100) # 0CNX_YRTL
write_sint(buf, -200) # y
write_uint(buf, 2) # repetition (3 cols.)
write_uint(buf, 1) # (repetition)
write_uint(buf, 10) # (repetition)
# TEXT 7
write_uint(buf, 19) # TEXT record
write_byte(buf, 0b0000_1100) # 0CNX_YRTL
write_sint(buf, -200) # y
write_uint(buf, 3) # repetition (4 cols.)
write_uint(buf, 2) # (repetition)
write_uint(buf, 12) # (repetition)
# TEXT 8
write_uint(buf, 19) # TEXT record
write_byte(buf, 0b0000_1100) # 0CNX_YRTL
write_sint(buf, -200) # y
write_uint(buf, 4) # repetition (4 arbitrary cols.)
write_uint(buf, 2) # (repetition)
write_uint(buf, 12) # (repetition)
write_uint(buf, 13) # (repetition)
write_uint(buf, 14) # (repetition)
# TEXT 9
write_uint(buf, 19) # TEXT record
write_byte(buf, 0b0000_1100) # 0CNX_YRTL
write_sint(buf, -200) # y
write_uint(buf, 5) # repetition (4 arbitrary cols., grid 3)
write_uint(buf, 2) # (repetition)
write_uint(buf, 3) # (repetition)
write_uint(buf, 4) # (repetition)
write_uint(buf, 5) # (repetition)
write_uint(buf, 6) # (repetition)
# TEXT 10
write_uint(buf, 19) # TEXT record
write_byte(buf, 0b0000_1100) # 0CNX_YRTL
write_sint(buf, -200) # y
write_uint(buf, 6) # repetition (4 arbitrary cols., grid 3)
write_uint(buf, 1) # (repetition)
write_uint(buf, 10) # (repetition)
write_uint(buf, 11) # (repetition)
# TEXT 11
write_uint(buf, 19) # TEXT record
write_byte(buf, 0b0000_1100) # 0CNX_YRTL
write_sint(buf, -200) # y
write_uint(buf, 7) # repetition (3 arbitrary cols., grid 5)
write_uint(buf, 1) # (repetition)
write_uint(buf, 5) # (repetition)
write_uint(buf, 2) # (repetition)
write_uint(buf, 3) # (repetition)
# TEXT 12
write_uint(buf, 19) # TEXT record
write_byte(buf, 0b0000_1100) # 0CNX_YRTL
write_sint(buf, -200) # y
write_uint(buf, 8) # repetition (3x4 matrix w/arb. vectors)
write_uint(buf, 1) # (repetition) n-dimension
write_uint(buf, 2) # (repetition) m-dimension
write_uint(buf, (10 << 4) | 0b0000) # (repetition) n-displacement g-delta: 10/east = (10, 0)
write_uint(buf, (11 << 2) | 0b11) # (repetition) m-displacement g-delta: (-11, -12)
write_sint(buf, -12) # (repetition g-delta)
# TEXT 13
write_uint(buf, 19) # TEXT record
write_byte(buf, 0b0000_1100) # 0CNX_YRTL
write_sint(buf, -200) # y
write_uint(buf, 8) # repetition (3x4 matrix w/arb. vectors)
write_uint(buf, 1) # (repetition) n-dimension
write_uint(buf, 2) # (repetition) m-dimension
write_uint(buf, (11 << 2) | 0b01) # (repetition) n-displacement g-delta: (11, 12)
write_sint(buf, 12)
write_uint(buf, (10 << 4) | 0b1010) # (repetition) n-displacement g-delta: 10/northwest = (-10, 10)
# TEXT 14
write_uint(buf, 19) # TEXT record
write_byte(buf, 0b0000_1100) # 0CNX_YRTL
write_sint(buf, -200) # y
write_uint(buf, 9) # repetition (3x arb. vector)
write_uint(buf, 1) # (repetition) dimension
write_uint(buf, (11 << 2) | 0b01) # (repetition) n-displacement g-delta: (11, 12)
write_sint(buf, 12) # (repetition g-delta)
# TEXT 15
write_uint(buf, 19) # TEXT record
write_byte(buf, 0b0000_1100) # 0CNX_YRTL
write_sint(buf, -200) # y
write_uint(buf, 9) # repetition (4x arb. vector)
write_uint(buf, 2) # (repetition) dimension
write_uint(buf, (10 << 4) | 0b1010) # (repetition) n-displacement g-delta: 10/northwest = (-10, 10)
# TEXT 16
write_uint(buf, 19) # TEXT record
write_byte(buf, 0b0000_1100) # 0CNX_YRTL
write_sint(buf, -200) # y
write_uint(buf, 10) # repetition (9x / 8 arb. displacements)
write_uint(buf, 7) # (repetition) dimension
write_uint(buf, (10 << 4) | 0b0000) # (repetition) g-delta: 10/east = (10, 0)
write_uint(buf, (10 << 4) | 0b0010) # (repetition) g-delta: 10/north = (0, 10)
write_uint(buf, (10 << 4) | 0b0100) # (repetition) g-delta: 10/west = (-10, 0)
if variant == 12:
write_uint(buf, (10 << 4) | 0b0110) # (repetition) g-delta: 10/south = (0, -10)
else:
write_uint(buf, (40 << 4) | 0b0110) # (repetition) g-delta: 40/south = (0, -40)
write_uint(buf, (10 << 4) | 0b1000) # (repetition) g-delta: 10/northeast = (10, 10)
write_uint(buf, (10 << 4) | 0b1010) # (repetition) g-delta: 10/northwest = (-10, 10)
write_uint(buf, (10 << 4) | 0b1100) # (repetition) g-delta: 10/southwest = (-10, -10)
if variant == 12:
write_uint(buf, (10 << 4) | 0b1110) # (repetition) g-delta: 20/southeast = (10, -10)
else:
write_uint(buf, (20 << 4) | 0b1110) # (repetition) g-delta: 20/southeast = (20, -20)
# TEXT 17
write_uint(buf, 19) # TEXT record
write_byte(buf, 0b0000_1100) # 0CNX_YRTL
write_sint(buf, -200) # y
write_uint(buf, 10) # repetition (3x / 2 arb. displacements)
write_uint(buf, 1) # (repetition) dimension
write_uint(buf, (11 << 2) | 0b11) # (repetition) g-delta: (-11, 12)
write_sint(buf, 12) # (repetition g-delta)
write_uint(buf, (10 << 4) | 0b1110) # (repetition) n-displacement g-delta: 10/southeast = (10, -10)
# TEXT 18
write_uint(buf, 19) # TEXT record
write_byte(buf, 0b0000_1100) # 0CNX_YRTL
write_sint(buf, -200) # y
write_uint(buf, 11) # repetition (9x / grid 2 / 8 arb. displacements)
write_uint(buf, 7) # (repetition) dimension (9)
write_uint(buf, 2) # (repetition) grid
write_uint(buf, ( 5 << 4) | 0b0000) # (repetition) g-delta: 10/east = (10, 0)
write_uint(buf, ( 5 << 4) | 0b0010) # (repetition) g-delta: 10/north = (0, 10)
write_uint(buf, ( 5 << 4) | 0b0100) # (repetition) g-delta: 10/west = (-10, 0)
if variant == 12:
write_uint(buf, (5 << 4) | 0b0110) # (repetition) g-delta: 10/south = (0, -10)
else:
write_uint(buf, (20 << 4) | 0b0110) # (repetition) g-delta: 40/south = (0, -40)
write_uint(buf, ( 5 << 4) | 0b1000) # (repetition) g-delta: 10/northeast = (10, 10)
write_uint(buf, ( 5 << 4) | 0b1010) # (repetition) g-delta: 10/northwest = (-10, 10)
write_uint(buf, ( 5 << 4) | 0b1100) # (repetition) g-delta: 10/southwest = (-10, -10)
if variant == 12:
write_uint(buf, (5 << 4) | 0b1110) # (repetition) g-delta: 20/southeast = (-10, -10)
else:
write_uint(buf, (10 << 4) | 0b1110) # (repetition) g-delta: 20/southeast = (-20, -20)
# TEXT 19
write_uint(buf, 19) # TEXT record
write_byte(buf, 0b0000_1100) # 0CNX_YRTL
write_sint(buf, -200) # y
write_uint(buf, 11) # repetition (3x / grid 3 / 2 arb. displacements)
write_uint(buf, 1) # (repetition) dimension
write_uint(buf, 3) # (repetition) grid
write_uint(buf, (4 << 2) | 0b11) # (repetition) g-delta: (-12, 12)
write_sint(buf, 4) # (repetition g-delta)
write_uint(buf, (3 << 4) | 0b1110) # (repetition) n-displacement g-delta: 9/southeast = (9, -9)
if variant == 12:
write_uint(buf, 6) # TEXTSTRING record (explicit id)
write_bstring(buf, b'A')
write_uint(buf, 1) # id
write_uint(buf, 6) # TEXTSTRING record (explicit id)
write_bstring(buf, b'B')
write_uint(buf, 2) # id
buf.write(FOOTER)
return buf
def test_file_1() -> None:
buf = write_file_common(BytesIO(), 1)
buf.seek(0)
layout = OasisLayout.read(buf)
base_tests(layout)
common_tests(layout)
geometry = layout.cells[0].geometry
for ii, gg in enumerate(geometry):
assert gg.string.string == 'TEXT_ABC', f'textstring #{ii}'
assert geometry[16].repetition.x_displacements == [10, 0, -10, 0, 10, -10, -10, 20]
assert geometry[16].repetition.y_displacements == [0, 10, 0, -40, 10, 10, -10, -20]
assert geometry[18].repetition.x_displacements == [10, 0, -10, 0, 10, -10, -10, 20]
assert geometry[18].repetition.y_displacements == [0, 10, 0, -40, 10, 10, -10, -20]
def test_file_2() -> None:
buf = write_file_common(BytesIO(), 2)
buf.seek(0)
layout = OasisLayout.read(buf)
base_tests(layout)
common_tests(layout)
geometry = layout.cells[0].geometry
for ii, gg in enumerate(geometry):
if ii in (1, 2, 3):
assert gg.string == 2, f'textstring #{ii}'
else:
assert gg.string == 1, f'textstring #{ii}'
assert geometry[16].repetition.x_displacements == [10, 0, -10, 0, 10, -10, -10, 20]
assert geometry[16].repetition.y_displacements == [0, 10, 0, -40, 10, 10, -10, -20]
assert geometry[18].repetition.x_displacements == [10, 0, -10, 0, 10, -10, -10, 20]
assert geometry[18].repetition.y_displacements == [0, 10, 0, -40, 10, 10, -10, -20]
assert layout.textstrings[1].string == 'A'
assert layout.textstrings[2].string == 'B'
def test_file_5() -> None:
buf = write_file_common(BytesIO(), 5)
buf.seek(0)
layout = OasisLayout.read(buf)
base_tests(layout)
common_tests(layout)
geometry = layout.cells[0].geometry
for ii, gg in enumerate(geometry):
if ii in (1, 2, 3):
assert gg.string == 0, f'textstring #{ii}'
else:
assert gg.string == 1, f'textstring #{ii}'
assert geometry[16].repetition.x_displacements == [10, 0, -10, 0, 10, -10, -10, 20]
assert geometry[16].repetition.y_displacements == [0, 10, 0, -40, 10, 10, -10, -20]
assert geometry[18].repetition.x_displacements == [10, 0, -10, 0, 10, -10, -10, 20]
assert geometry[18].repetition.y_displacements == [0, 10, 0, -40, 10, 10, -10, -20]
assert layout.textstrings[0].string == 'A'
assert layout.textstrings[1].string == 'B'
def test_file_12() -> None:
buf = write_file_common(BytesIO(), 12)
buf.seek(0)
layout = OasisLayout.read(buf)
base_tests(layout)
common_tests(layout)
geometry = layout.cells[0].geometry
for ii, gg in enumerate(geometry):
if ii in (1, 2, 3):
assert gg.string == 2, f'textstring #{ii}'
else:
assert gg.string == 1, f'textstring #{ii}'
assert geometry[16].repetition.x_displacements == [10, 0, -10, 0, 10, -10, -10, 10]
assert geometry[16].repetition.y_displacements == [0, 10, 0, -10, 10, 10, -10, -10]
assert geometry[18].repetition.x_displacements == [10, 0, -10, 0, 10, -10, -10, 10]
assert geometry[18].repetition.y_displacements == [0, 10, 0, -10, 10, 10, -10, -10]
assert layout.textstrings[1].string == 'A'
assert layout.textstrings[2].string == 'B'
def write_file_3(buf: IO[bytes]) -> IO[bytes]:
"""
File with one textstring with explicit id, and one with an implicit id.
Should fail.
"""
buf.write(HEADER)
write_uint(buf, 6) # TEXTSTRING record (explicit id)
write_bstring(buf, b'A')
write_uint(buf, 1) # id
write_uint(buf, 5) # TEXTSTRING record (implicit id 0) (FAIL due to mix)
write_bstring(buf, b'B')
write_uint(buf, 14) # CELL record (explicit)
write_bstring(buf, b'ABC') # Cell name
write_uint(buf, 19) # TEXT record
write_byte(buf, 0b0111_1011) # 0CNX_YRTL
write_uint(buf, 1) # textstring id
write_uint(buf, 1) # layer
write_uint(buf, 2) # datatype
write_sint(buf, 100) # x
write_sint(buf, -200) # y
buf.write(FOOTER)
return buf
def test_file_3() -> None:
buf = write_file_3(BytesIO())
buf.seek(0)
with pytest.raises(InvalidRecordError):
_layout = OasisLayout.read(buf)
def write_file_4(buf: IO[bytes]) -> IO[bytes]:
"""
File with a TEXT record that references a non-existent TEXTSTRING
TODO add an optional check for valid references
"""
buf.write(HEADER)
write_uint(buf, 5) # TEXTSTRING record (implicit id 0)
write_bstring(buf, b'A')
write_uint(buf, 5) # TEXTSTRING record (implicit id 1)
write_bstring(buf, b'B')
write_uint(buf, 14) # CELL record (explicit)
write_bstring(buf, b'ABC') # Cell name
write_uint(buf, 19) # TEXT record
write_byte(buf, 0b0111_1011) # 0CNX_YRTL
write_uint(buf, 2) # textstring id # INVALID ID
write_uint(buf, 1) # layer
write_uint(buf, 2) # datatype
write_sint(buf, 100) # x
write_sint(buf, -200) # y
buf.write(FOOTER)
return buf
def test_file_4() -> None:
buf = write_file_4(BytesIO())
buf.seek(0)
# with pytest.raises(InvalidRecordError):
layout = OasisLayout.read(buf)
# TODO: check for invalid textstring references
base_tests(layout)
def write_file_6(buf: IO[bytes]) -> IO[bytes]:
"""
File with TEXT record that uses an un-filled modal for the repetition
"""
buf.write(HEADER)
write_uint(buf, 5) # TEXTSTRING record (implicit id 0)
write_bstring(buf, b'A')
write_uint(buf, 14) # CELL record (explicit)
write_bstring(buf, b'ABC') # Cell name
write_uint(buf, 19) # TEXT record
write_byte(buf, 0b0111_1111) # 0CNX_YRTL
write_uint(buf, 0) # textstring id
write_uint(buf, 1) # layer
write_uint(buf, 2) # datatype
write_sint(buf, 100) # x
write_sint(buf, -200) # y
write_uint(buf, 0) # reuse repetition (FAIL due to empty modal)
buf.write(FOOTER)
return buf
def test_file_6() -> None:
buf = write_file_6(BytesIO())
buf.seek(0)
with pytest.raises(InvalidDataError):
_layout = OasisLayout.read(buf)
def write_file_7(buf: IO[bytes]) -> IO[bytes]:
"""
File with TEXT record that uses an un-filled modal for the layer
"""
buf.write(HEADER)
write_uint(buf, 5) # TEXTSTRING record (implicit id 0)
write_bstring(buf, b'A')
write_uint(buf, 14) # CELL record (explicit)
write_bstring(buf, b'ABC') # Cell name
write_uint(buf, 19) # TEXT record
write_byte(buf, 0b0111_1010) # 0CNX_YRTL
write_uint(buf, 0) # textstring id
write_uint(buf, 2) # datatype
write_sint(buf, 100) # x
write_sint(buf, -200) # y
buf.write(FOOTER)
return buf
def test_file_7() -> None:
buf = write_file_7(BytesIO())
buf.seek(0)
with pytest.raises(InvalidDataError):
_layout = OasisLayout.read(buf)
def write_file_8(buf: IO[bytes]) -> IO[bytes]:
"""
File with TEXT record that uses an un-filled modal for the datatype
"""
buf.write(HEADER)
write_uint(buf, 5) # TEXTSTRING record (implicit id 0)
write_bstring(buf, b'A')
write_uint(buf, 14) # CELL record (explicit)
write_bstring(buf, b'ABC') # Cell name
write_uint(buf, 19) # TEXT record
write_byte(buf, 0b0111_1001) # 0CNX_YRTL
write_uint(buf, 0) # textstring id
write_uint(buf, 1) # layer
write_sint(buf, 100) # x
write_sint(buf, -200) # y
buf.write(FOOTER)
return buf
def test_file_8() -> None:
buf = write_file_8(BytesIO())
buf.seek(0)
with pytest.raises(InvalidDataError):
_layout = OasisLayout.read(buf)
def write_file_9(buf: IO[bytes]) -> IO[bytes]:
"""
File with TEXT record that uses a default modal for the x coordinate
"""
buf.write(HEADER)
write_uint(buf, 5) # TEXTSTRING record (implicit id 0)
write_bstring(buf, b'A')
write_uint(buf, 14) # CELL record (explicit)
write_bstring(buf, b'ABC') # Cell name
write_uint(buf, 19) # TEXT record
write_byte(buf, 0b0110_1011) # 0CNX_YRTL
write_uint(buf, 0) # textstring id
write_uint(buf, 1) # layer
write_uint(buf, 2) # datatype
write_sint(buf, -200) # y
buf.write(FOOTER)
return buf
def test_file_9() -> None:
buf = write_file_9(BytesIO())
buf.seek(0)
layout = OasisLayout.read(buf)
text = layout.cells[0].geometry[0]
assert text.x == 0
assert text.layer == 1
assert text.datatype == 2
assert text.y == -200
def write_file_10(buf: IO[bytes]) -> IO[bytes]:
"""
File with TEXT record that uses a default modal for the y coordinate
"""
buf.write(HEADER)
write_uint(buf, 5) # TEXTSTRING record (implicit id 0)
write_bstring(buf, b'A')
write_uint(buf, 14) # CELL record (explicit)
write_bstring(buf, b'ABC') # Cell name
write_uint(buf, 19) # TEXT record
write_byte(buf, 0b0111_0011) # 0CNX_YRTL
write_uint(buf, 0) # textstring id
write_uint(buf, 1) # layer
write_uint(buf, 2) # datatype
write_sint(buf, 100) # x
buf.write(FOOTER)
return buf
def test_file_10() -> None:
buf = write_file_10(BytesIO())
buf.seek(0)
layout = OasisLayout.read(buf)
text = layout.cells[0].geometry[0]
assert text.y == 0
assert text.layer == 1
assert text.datatype == 2
assert text.x == 100
def write_file_11(buf: IO[bytes]) -> IO[bytes]:
"""
File with TEXT record that uses an un-filled modal for the text string
"""
buf.write(HEADER)
write_uint(buf, 5) # TEXTSTRING record (implicit id 0)
write_bstring(buf, b'A')
write_uint(buf, 14) # CELL record (explicit)
write_bstring(buf, b'ABC') # Cell name
write_uint(buf, 19) # TEXT record
write_byte(buf, 0b0001_1011) # 0CNX_YRTL
write_uint(buf, 1) # layer
write_uint(buf, 2) # datatype
write_sint(buf, 100) # x
write_sint(buf, -200) # y
buf.write(FOOTER)
return buf
def test_file_11() -> None:
buf = write_file_11(BytesIO())
buf.seek(0)
with pytest.raises(InvalidDataError):
_layout = OasisLayout.read(buf)

View File

@ -1,226 +0,0 @@
# mypy: disable-error-code="union-attr"
from typing import IO
from io import BytesIO
from .utils import HEADER, FOOTER
from ..basic import write_uint, write_sint, write_bstring, write_byte
from ..main import OasisLayout
def base_tests(layout: OasisLayout) -> None:
assert layout.version.string == '1.0'
assert layout.unit == 1000
assert layout.validation.checksum_type == 0
assert not layout.properties
assert not layout.propnames
assert not layout.xnames
assert not layout.textstrings
assert not layout.cellnames
assert not layout.layers
assert len(layout.cells) == 1
assert layout.cells[0].name.string == 'ABC'
assert not layout.cells[0].properties
def write_file_1(buf: IO[bytes]) -> IO[bytes]:
"""
"""
buf.write(HEADER)
write_uint(buf, 14) # CELL record (explicit)
write_bstring(buf, b'ABC') # Cell name
# Trapezoid 0
write_uint(buf, 23) # TRAPEZOID record
write_byte(buf, 0b0111_1011) # OWHX_YRDL
write_uint(buf, 1) # layer
write_uint(buf, 2) # datatype
write_uint(buf, 100) # width
write_uint(buf, 50) # height
write_sint(buf, -20) # delta-a
write_sint(buf, 40) # delta-b
write_sint(buf, 0) # geometry-x (absolute)
write_sint(buf, 100) # geometry-y (absolute)
write_uint(buf, 16) # XYRELATIVE record
# Trapezoid 1
write_uint(buf, 23) # TRAPEZOID record
write_byte(buf, 0b1010_1011) # OWHX_YRDL
write_uint(buf, 1) # layer
write_uint(buf, 2) # datatype
write_uint(buf, 50) # height
write_sint(buf, 20) # delta-a
write_sint(buf, 40) # delta-b
write_sint(buf, 300) # geometry-y (absolute)
# Trapezoid 2
write_uint(buf, 23) # TRAPEZOID record
write_byte(buf, 0b1100_1001) # OWHX_YRDL
write_uint(buf, 1) # layer
write_uint(buf, 150) # width
write_sint(buf, 20) # delta-a
write_sint(buf, -20) # delta-b
write_sint(buf, 300) # geometry-y (relative)
# Trapezoid 3
write_uint(buf, 23) # TRAPEZOID record
write_byte(buf, 0b0100_1101) # OWHX_YRDL
write_uint(buf, 1) # layer
write_uint(buf, 150) # width
write_sint(buf, 20) # delta-a
write_sint(buf, -20) # delta-b
write_sint(buf, 300) # geometry-y (relative)
write_uint(buf, 1) # repetition (3x4 matrix)
write_uint(buf, 1) # (repetition) x-dimension
write_uint(buf, 2) # (repetition) y-dimension
write_uint(buf, 200) # (repetition) x-spacing
write_uint(buf, 300) # (repetition) y-spacing
write_uint(buf, 15) # XYABSOLUTE record
# Trapezoid 4
write_uint(buf, 24) # TRAPEZOID record
write_byte(buf, 0b0111_1011) # OWHX_YRDL
write_uint(buf, 1) # layer
write_uint(buf, 2) # datatype
write_uint(buf, 100) # width
write_uint(buf, 50) # height
write_sint(buf, -20) # delta-a
write_sint(buf, 1000) # geometry-x (absolute)
write_sint(buf, 100) # geometry-y (absolute)
write_uint(buf, 16) # XYRELATIVE record
# Trapezoid 5
write_uint(buf, 24) # TRAPEZOID record
write_byte(buf, 0b1010_1011) # OWHX_YRDL
write_uint(buf, 1) # layer
write_uint(buf, 2) # datatype
write_uint(buf, 50) # height
write_sint(buf, 20) # delta-a
write_sint(buf, 300) # geometry-y (relative)
# Trapezoid 6
write_uint(buf, 24) # TRAPEZOID record
write_byte(buf, 0b1100_1001) # OWHX_YRDL
write_uint(buf, 1) # layer
write_uint(buf, 150) # width
write_sint(buf, 20) # delta-a
write_sint(buf, 300) # geometry-y (relative)
# Trapezoid 7
write_uint(buf, 24) # TRAPEZOID record
write_byte(buf, 0b0100_1101) # OWHX_YRDL
write_uint(buf, 1) # layer
write_uint(buf, 150) # width
write_sint(buf, 20) # delta-a
write_sint(buf, 300) # geometry-y (relative)
write_uint(buf, 1) # repetition (3x4 matrix)
write_uint(buf, 1) # (repetition) x-dimension
write_uint(buf, 2) # (repetition) y-dimension
write_uint(buf, 200) # (repetition) x-spacing
write_uint(buf, 300) # (repetition) y-spacing
write_uint(buf, 15) # XYABSOLUTE record
# Trapezoid 8
write_uint(buf, 25) # TRAPEZOID record
write_byte(buf, 0b0111_1011) # OWHX_YRDL
write_uint(buf, 1) # layer
write_uint(buf, 2) # datatype
write_uint(buf, 100) # width
write_uint(buf, 50) # height
write_sint(buf, 40) # delta-b
write_sint(buf, 2000) # geometry-x (absolute)
write_sint(buf, 100) # geometry-y (absolute)
write_uint(buf, 16) # XYRELATIVE record
# Trapezoid 9
write_uint(buf, 25) # TRAPEZOID record
write_byte(buf, 0b1010_1011) # OWHX_YRDL
write_uint(buf, 1) # layer
write_uint(buf, 2) # datatype
write_uint(buf, 50) # height
write_sint(buf, 40) # delta-b
write_sint(buf, 300) # geometry-y (relative)
# Trapezoid 10
write_uint(buf, 25) # TRAPEZOID record
write_byte(buf, 0b1100_1001) # OWHX_YRDL
write_uint(buf, 1) # layer
write_uint(buf, 150) # width
write_sint(buf, -20) # delta-b
write_sint(buf, 300) # geometry-y (relative)
# Trapezoid 11
write_uint(buf, 25) # TRAPEZOID record
write_byte(buf, 0b0100_1101) # OWHX_YRDL
write_uint(buf, 1) # layer
write_uint(buf, 150) # width
write_sint(buf, -20) # delta-b
write_sint(buf, 300) # geometry-y (relative)
write_uint(buf, 1) # repetition (3x4 matrix)
write_uint(buf, 1) # (repetition) x-dimension
write_uint(buf, 2) # (repetition) y-dimension
write_uint(buf, 200) # (repetition) x-spacing
write_uint(buf, 300) # (repetition) y-spacing
buf.write(FOOTER)
return buf
def test_file_1() -> None:
buf = write_file_1(BytesIO())
buf.seek(0)
layout = OasisLayout.read(buf)
base_tests(layout)
geometry = layout.cells[0].geometry
assert len(geometry) == 12
for ii, gg in enumerate(geometry):
msg = f'Failed on trapezoid {ii}'
assert gg.x == 1000 * (ii // 4), msg
assert gg.y == 100 + 300 * (ii % 4), msg
assert gg.layer == 1, msg
assert gg.datatype == 2, msg
if ii % 4 == 3:
assert gg.repetition.a_count == 3, msg
assert gg.repetition.b_count == 4, msg
assert gg.repetition.a_vector == [200, 0], msg
assert gg.repetition.b_vector == [0, 300], msg
else:
assert gg.repetition is None, msg
assert not gg.properties, msg
assert gg.height == 50, msg
if ii % 4 < 2:
assert gg.width == 100, msg
else:
assert gg.width == 150, msg
if ii in (0, 4):
assert gg.delta_a == -20, msg
elif ii >= 8:
assert gg.delta_a == 0, msg
else:
assert gg.delta_a == 20, msg
if ii in (0, 1, 8, 9):
assert gg.delta_b == 40, msg
elif 4 <= ii < 8:
assert gg.delta_b == 0, msg
else:
assert gg.delta_b == -20, msg
assert gg.is_vertical == ((ii % 4) in (1, 2)), msg
assert not gg.properties

View File

@ -1,68 +0,0 @@
from itertools import chain
from io import BytesIO
from ..basic import read_uint, read_sint, write_uint, write_sint
uints = (
( 0, '00'),
( 127, '7f'),
( 128, '80 01'),
(16_383, 'ff 7f'),
(16_384, '80 80 01'),
)
uints_readonly = (
( 0, '80 80 00'),
)
sints = (
( 0, '00'),
( 1, '02'),
( -1, '03'),
( 63, '7e'),
( -64, '81 01'),
( 8191, 'fe 7f'),
( -8192, '81 80 01'),
)
sints_readonly = (
)
def test_read_uint() -> None:
buffer = BytesIO(bytes.fromhex(
''.join([hh for _ii, hh in chain(uints, uints_readonly)])))
for ii, _hh in chain(uints, uints_readonly):
assert read_uint(buffer) == ii
def test_write_uint() -> None:
buffer = BytesIO()
for ii, _hh in uints:
write_uint(buffer, ii)
correct_bytes = bytes.fromhex(
''.join([hh for _ii, hh in uints]))
assert buffer.getbuffer() == correct_bytes
def test_read_sint() -> None:
buffer = BytesIO(bytes.fromhex(
''.join([hh for _ii, hh in chain(sints, sints_readonly)])))
for ii, _hh in chain(sints, sints_readonly):
assert read_sint(buffer) == ii
def test_write_sint() -> None:
buffer = BytesIO()
for ii, _hh in sints:
write_sint(buffer, ii)
correct_bytes = bytes.fromhex(
''.join([hh for _ii, hh in sints]))
assert buffer.getbuffer() == correct_bytes

View File

@ -1,41 +0,0 @@
from io import BytesIO
from ..basic import write_uint, write_bstring, write_byte
MAGIC_BYTES = b'%SEMI-OASIS\r\n'
def _gen_header() -> bytes:
buf = BytesIO()
buf.write(MAGIC_BYTES)
write_uint(buf, 1) # START record
write_bstring(buf, b'1.0') # version
write_uint(buf, 0) # dbu real type: uint
write_uint(buf, 1000) # dbu value: 1000 per micron
write_uint(buf, 0) # offset table is present here
for _ in range(6):
write_uint(buf, 0) # offset table (0: not strict)
write_uint(buf, 0) # offset table (0: no entry present)
return buf.getvalue()
def _gen_footer() -> bytes:
buf = BytesIO()
write_uint(buf, 2) # END record
# 254-byte padding, (0-byte bstring with length 0;
# length is written as 0x80 0x80 ... 0x80 0x00)
for _ in range(253):
write_byte(buf, 0x80)
write_byte(buf, 0)
write_uint(buf, 0) # no validation
return buf.getvalue()
HEADER = _gen_header()
FOOTER = _gen_footer()

View File

@ -1,90 +0,0 @@
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "fatamorgana"
description = "OASIS layout format parser and writer"
readme = "README.md"
license = { file = "LICENSE.md" }
authors = [
{ name="Jan Petykiewicz", email="jan@mpxd.net" },
]
homepage = "https://mpxd.net/code/jan/fatamorgana"
repository = "https://mpxd.net/code/jan/fatamorgana"
keywords = [
"OASIS",
"layout",
"design",
"CAD",
"EDA",
"oas",
"electronics",
"open",
"artwork",
"interchange",
"standard",
"mask",
"pattern",
"IC",
"geometry",
"geometric",
"polygon",
"gds",
]
classifiers = [
"Programming Language :: Python :: 3",
"Development Status :: 3 - Alpha",
"Environment :: Other Environment",
"Intended Audience :: Developers",
"Intended Audience :: Information Technology",
"Intended Audience :: Manufacturing",
"Intended Audience :: Science/Research",
"License :: OSI Approved :: GNU Affero General Public License v3",
"Topic :: Scientific/Engineering",
"Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)",
]
requires-python = ">=3.11"
dynamic = ["version"]
dependencies = [
]
[tool.hatch.version]
path = "fatamorgana/__init__.py"
[project.optional-dependencies]
numpy = ["numpy>=1.26"]
[tool.ruff]
exclude = [
".git",
"dist",
]
line-length = 145
indent-width = 4
lint.dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
lint.select = [
"NPY", "E", "F", "W", "B", "ANN", "UP", "SLOT", "SIM", "LOG",
"C4", "ISC", "PIE", "PT", "RET", "TCH", "PTH", "INT",
"ARG", "PL", "R", "TRY",
"G010", "G101", "G201", "G202",
"Q002", "Q003", "Q004",
]
lint.ignore = [
#"ANN001", # No annotation
"ANN002", # *args
"ANN003", # **kwargs
"ANN401", # Any
"ANN101", # self: Self
"SIM108", # single-line if / else assignment
"RET504", # x=y+z; return x
"PIE790", # unnecessary pass
"ISC003", # non-implicit string concatenation
"C408", # dict(x=y) instead of {'x': y}
"PLR09", # Too many xxx
"PLR2004", # magic number
"PLC0414", # import x as x
"TRY003", # Long exception message
]

61
setup.py Normal file
View File

@ -0,0 +1,61 @@
#!/usr/bin/env python3
from setuptools import setup, find_packages
import fatamorgana
with open('README.md', 'r') as f:
long_description = f.read()
setup(name='fatamorgana',
version=fatamorgana.version,
description='OASIS layout format parser and writer',
long_description=long_description,
long_description_content_type='text/markdown',
author='Jan Petykiewicz',
author_email='anewusername@gmail.com',
url='https://mpxd.net/code/jan/fatamorgana',
keywords=[
'OASIS',
'layout',
'design',
'CAD',
'EDA',
'oas',
'electronics',
'open',
'artwork',
'interchange',
'standard',
'mask',
'pattern',
'IC',
'geometry',
'geometric',
'polygon',
'gds',
],
classifiers=[
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Development Status :: 3 - Alpha',
'Environment :: Other Environment',
'Intended Audience :: Developers',
'Intended Audience :: Information Technology',
'Intended Audience :: Manufacturing',
'Intended Audience :: Science/Research',
'License :: OSI Approved :: GNU Affero General Public License v3',
'Operating System :: POSIX :: Linux',
'Operating System :: Microsoft :: Windows',
'Topic :: Scientific/Engineering',
'Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)',
'Topic :: Software Development :: Libraries :: Python Modules',
],
packages=find_packages(),
install_requires=[
'typing',
],
extras_require={
'numpy': ['numpy'],
},
)