Compare commits

..

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

32 changed files with 2155 additions and 8487 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 *.pyc
__pycache__ __pycache__
*.idea *.idea
.mypy_cache/
build build
dist dist
fatamorgana.egg-info fatamorgana.egg-info
docs
*.gds
*.gds.gz
*.oas
*.oas.gz

3
MANIFEST.in Normal file
View File

@ -0,0 +1,3 @@
include README.md
include LICENSE.md
include fatamorgana/VERSION

View File

@ -1,14 +1,12 @@
# fatamorgana # fatamorgana
**fatamorgana** is a Python package for reading and writing OASIS format layout files. **fatamorgana** is a Python package for reading and writing OASIS format layout files.
**Homepage:** https://mpxd.net/code/jan/fatamorgana **Homepage:** https://mpxd.net/code/jan/fatamorgana
* [PyPI](https://pypi.org/project/fatamorgana)
* [Github mirror](https://github.com/anewusername/fatamorgana)
**Capabilities:** **Capabilities:**
* This package is a work-in-progress and is largely untested -- it works for * This package is a work-in-progress and is largely untested -- it works for
the tasks I usually use it for, but I can't guarantee I've even the tasks I usually use it for, but I can't guarantee I've even
tried the features you happen to use! Use at your own risk! tried the features you happen to use! Use at your own risk!
* Interfaces and datastructures are subject to change! * Interfaces and datastructures are subject to change!
* That said the following work for me: * That said the following work for me:
@ -22,18 +20,18 @@
## Installation ## Installation
**Dependencies:** **Dependencies:**
* python >=3.11 * python 3.5 or newer
* (optional) numpy * (optional) numpy
Install with pip from PyPi (preferred): Install with pip from PyPi (preferred):
```bash ```bash
pip3 install fatamorgana pip install fatamorgana
``` ```
Install directly from git repository: Install directly from git repository:
```bash ```bash
pip3 install git+https://mpxd.net/code/jan/fatamorgana.git@release pip install git+https://mpxd.net/code/jan/fatamorgana.git@release
``` ```
## Documentation ## Documentation
@ -55,7 +53,7 @@ Read an OASIS file and write it back out:
with open('test.oas', 'rb') as f: with open('test.oas', 'rb') as f:
layout = fatamorgana.OasisLayout.read(f) layout = fatamorgana.OasisLayout.read(f)
with open('test_write.oas', 'wb') as f: with open('test_write.oas', 'wb') as f:
layout.write(f) layout.write(f)
``` ```

View File

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

View File

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

1
fatamorgana/VERSION Normal file
View File

@ -0,0 +1 @@
0.5

View File

@ -15,37 +15,19 @@
numpy to speed up reading/writing. numpy to speed up reading/writing.
Dependencies: Dependencies:
- Python 3.11 or later - Python 3.5 or later
- numpy (optional, faster but no additional functionality) - numpy (optional, no additional functionality)
To get started, try:
```python3
import fatamorgana
help(fatamorgana.OasisLayout)
```
""" """
from .main import ( import pathlib
OasisLayout as OasisLayout,
Cell as Cell, from .main import OasisLayout, Cell, XName
XName as XName, from .basic import NString, AString, Validation, OffsetTable, OffsetEntry, \
) EOFError, SignedError, InvalidDataError, InvalidRecordError
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,
)
__author__ = 'Jan Petykiewicz' __author__ = 'Jan Petykiewicz'
__version__ = '0.13'
with open(pathlib.Path(__file__).parent / 'VERSION', 'r') as f:
__version__ = f.read().strip()
version = __version__ version = __version__

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

64
setup.py Normal file
View File

@ -0,0 +1,64 @@
#!/usr/bin/env python3
from setuptools import setup, find_packages
with open('README.md', 'r') as f:
long_description = f.read()
with open('fatamorgana/VERSION', 'r') as f:
version = f.read().strip()
setup(name='fatamorgana',
version=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 :: 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)',
],
packages=find_packages(),
package_data={
'fatamorgana': ['VERSION']
},
install_requires=[
'typing',
],
extras_require={
'numpy': ['numpy'],
},
)