Compare commits
	
		
			No commits in common. "master" and "release" have entirely different histories.
		
	
	
		
	
		
							
								
								
									
										30
									
								
								.flake8
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								.flake8
									
									
									
									
									
								
							@ -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
									
									
								
							
							
						
						
									
										7
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@ -1,14 +1,7 @@
 | 
			
		||||
*.pyc
 | 
			
		||||
__pycache__
 | 
			
		||||
*.idea
 | 
			
		||||
.mypy_cache/
 | 
			
		||||
 | 
			
		||||
build
 | 
			
		||||
dist
 | 
			
		||||
fatamorgana.egg-info
 | 
			
		||||
docs
 | 
			
		||||
 | 
			
		||||
*.gds
 | 
			
		||||
*.gds.gz
 | 
			
		||||
*.oas
 | 
			
		||||
*.oas.gz
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										2
									
								
								MANIFEST.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								MANIFEST.in
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,2 @@
 | 
			
		||||
include README.md
 | 
			
		||||
include LICENSE.md
 | 
			
		||||
@ -3,8 +3,6 @@
 | 
			
		||||
**fatamorgana** is a Python package for reading and writing OASIS format layout files.
 | 
			
		||||
 | 
			
		||||
**Homepage:** https://mpxd.net/code/jan/fatamorgana
 | 
			
		||||
* [PyPI](https://pypi.org/project/fatamorgana)
 | 
			
		||||
* [Github mirror](https://github.com/anewusername/fatamorgana)
 | 
			
		||||
 | 
			
		||||
**Capabilities:**
 | 
			
		||||
* This package is a work-in-progress and is largely untested -- it works for
 | 
			
		||||
@ -22,18 +20,18 @@
 | 
			
		||||
## Installation
 | 
			
		||||
 | 
			
		||||
**Dependencies:**
 | 
			
		||||
* python >=3.11
 | 
			
		||||
* python 3.5 or newer
 | 
			
		||||
* (optional) numpy
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Install with pip from PyPi (preferred):
 | 
			
		||||
```bash
 | 
			
		||||
pip3 install fatamorgana
 | 
			
		||||
pip install fatamorgana
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Install directly from git repository:
 | 
			
		||||
```bash
 | 
			
		||||
pip3 install git+https://mpxd.net/code/jan/fatamorgana.git@release
 | 
			
		||||
pip install git+https://mpxd.net/code/jan/fatamorgana.git@release
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Documentation
 | 
			
		||||
 | 
			
		||||
@ -1 +0,0 @@
 | 
			
		||||
../LICENSE.md
 | 
			
		||||
@ -1 +0,0 @@
 | 
			
		||||
../README.md
 | 
			
		||||
@ -15,37 +15,14 @@
 | 
			
		||||
  numpy to speed up reading/writing.
 | 
			
		||||
 | 
			
		||||
 Dependencies:
 | 
			
		||||
    - Python 3.11 or later
 | 
			
		||||
    - numpy (optional, faster but no additional functionality)
 | 
			
		||||
 | 
			
		||||
 To get started, try:
 | 
			
		||||
 ```python3
 | 
			
		||||
    import fatamorgana
 | 
			
		||||
    help(fatamorgana.OasisLayout)
 | 
			
		||||
 ```
 | 
			
		||||
    - Python 3.5 or later
 | 
			
		||||
    - numpy (optional, no additional functionality)
 | 
			
		||||
"""
 | 
			
		||||
from .main import (
 | 
			
		||||
    OasisLayout as OasisLayout,
 | 
			
		||||
    Cell as Cell,
 | 
			
		||||
    XName as XName,
 | 
			
		||||
    )
 | 
			
		||||
from .basic import (
 | 
			
		||||
    NString as NString,
 | 
			
		||||
    AString as AString,
 | 
			
		||||
    Validation as Validation,
 | 
			
		||||
    OffsetTable as OffsetTable,
 | 
			
		||||
    OffsetEntry as OffsetEntry,
 | 
			
		||||
    EOFError as EOFError,
 | 
			
		||||
    SignedError as SignedError,
 | 
			
		||||
    InvalidDataError as InvalidDataError,
 | 
			
		||||
    InvalidRecordError as InvalidRecordError,
 | 
			
		||||
    UnfilledModalError as UnfilledModalError,
 | 
			
		||||
    ReuseRepetition as ReuseRepetition,
 | 
			
		||||
    GridRepetition as GridRepetition,
 | 
			
		||||
    ArbitraryRepetition as ArbitraryRepetition,
 | 
			
		||||
    )
 | 
			
		||||
from .main import OasisLayout, Cell, XName
 | 
			
		||||
from .basic import NString, AString, Validation, OffsetTable, OffsetEntry, \
 | 
			
		||||
        EOFError, SignedError, InvalidDataError, InvalidRecordError
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__author__ = 'Jan Petykiewicz'
 | 
			
		||||
__version__ = '0.13'
 | 
			
		||||
version = __version__
 | 
			
		||||
 | 
			
		||||
version = '0.4'
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										1521
									
								
								fatamorgana/basic.py
									
									
									
									
									
								
							
							
						
						
									
										1521
									
								
								fatamorgana/basic.py
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -3,22 +3,18 @@ This module contains data structures and functions for reading from and
 | 
			
		||||
 writing to whole OASIS layout files, and provides a few additional
 | 
			
		||||
 abstractions for the data contained inside them.
 | 
			
		||||
"""
 | 
			
		||||
from typing import IO
 | 
			
		||||
import io
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
from . import records
 | 
			
		||||
from .records import Modals, Record
 | 
			
		||||
from .basic import (
 | 
			
		||||
    OffsetEntry, OffsetTable, NString, AString, real_t, Validation,
 | 
			
		||||
    read_magic_bytes, write_magic_bytes, read_uint, EOFError,
 | 
			
		||||
    InvalidRecordError,
 | 
			
		||||
    )
 | 
			
		||||
from .records import Modals
 | 
			
		||||
from .basic import OffsetEntry, OffsetTable, NString, AString, real_t, Validation, \
 | 
			
		||||
        read_magic_bytes, write_magic_bytes, read_uint, EOFError, \
 | 
			
		||||
        InvalidDataError, InvalidRecordError
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__author__ = 'Jan Petykiewicz'
 | 
			
		||||
 | 
			
		||||
#logging.basicConfig(level=logging.DEBUG)
 | 
			
		||||
 | 
			
		||||
logger = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
@ -27,21 +23,17 @@ class FileModals:
 | 
			
		||||
    """
 | 
			
		||||
    File-scoped modal variables
 | 
			
		||||
    """
 | 
			
		||||
    cellname_implicit: bool | None = None
 | 
			
		||||
    propname_implicit: bool | None = None
 | 
			
		||||
    xname_implicit: bool | None = None
 | 
			
		||||
    textstring_implicit: bool | None = None
 | 
			
		||||
    propstring_implicit: bool | None = None
 | 
			
		||||
    cellname_implicit = None        # type: bool or None
 | 
			
		||||
    propname_implicit = None        # type: bool or None
 | 
			
		||||
    xname_implicit = None           # type: bool or None
 | 
			
		||||
    textstring_implicit = None      # type: bool or None
 | 
			
		||||
    propstring_implicit = None      # type: bool or None
 | 
			
		||||
    cellname_implicit = None        # type: bool or None
 | 
			
		||||
 | 
			
		||||
    property_target: list[records.Property]
 | 
			
		||||
 | 
			
		||||
    within_cell: bool = False
 | 
			
		||||
    within_cblock: bool = False
 | 
			
		||||
    end_has_offset_table: bool = False
 | 
			
		||||
    started: bool = False
 | 
			
		||||
 | 
			
		||||
    def __init__(self, property_target: list[records.Property]) -> None:
 | 
			
		||||
        self.property_target = property_target
 | 
			
		||||
    within_cell = False             # type: bool
 | 
			
		||||
    within_cblock = False           # type: bool
 | 
			
		||||
    end_has_offset_table = None     # type: bool
 | 
			
		||||
    started = False                 # type: bool
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class OasisLayout:
 | 
			
		||||
@ -51,56 +43,49 @@ class OasisLayout:
 | 
			
		||||
    Names and strings are stored in dicts, indexed by reference number.
 | 
			
		||||
    Layer names and properties are stored directly using their associated
 | 
			
		||||
        record objects.
 | 
			
		||||
    Cells are stored using `Cell` objects (different from `records.Cell`
 | 
			
		||||
        record objects).
 | 
			
		||||
    Cells are stored using Cell objects (different from Cell record objects).
 | 
			
		||||
 | 
			
		||||
    Properties:
 | 
			
		||||
        File properties:
 | 
			
		||||
            .version            AString: Version string ('1.0')
 | 
			
		||||
            .unit               real number: grid steps per micron
 | 
			
		||||
            .validation         Validation: checksum data
 | 
			
		||||
 | 
			
		||||
        Names:
 | 
			
		||||
            .cellnames          Dict[int, NString]
 | 
			
		||||
            .propnames          Dict[int, NString]
 | 
			
		||||
            .xnames             Dict[int, XName]
 | 
			
		||||
 | 
			
		||||
        Strings:
 | 
			
		||||
            .textstrings        Dict[int, AString]
 | 
			
		||||
            .propstrings        Dict[int, AString]
 | 
			
		||||
 | 
			
		||||
        Data:
 | 
			
		||||
            .layers             List[records.LayerName]
 | 
			
		||||
            .properties         List[records.Property]
 | 
			
		||||
            .cells              List[Cell]
 | 
			
		||||
    """
 | 
			
		||||
    # File properties
 | 
			
		||||
    version: AString
 | 
			
		||||
    """File format version string ('1.0')"""
 | 
			
		||||
    version = None                  # type: AString
 | 
			
		||||
    unit = None                     # type: real_t
 | 
			
		||||
    validation = None               # type: Validation
 | 
			
		||||
 | 
			
		||||
    unit: real_t
 | 
			
		||||
    """grid steps per micron"""
 | 
			
		||||
    properties = None               # type: List[records.Property]
 | 
			
		||||
    cells = None                    # type: List[Cell]
 | 
			
		||||
 | 
			
		||||
    validation: Validation
 | 
			
		||||
    """checksum data"""
 | 
			
		||||
    cellnames = None                # type: Dict[int, NString]
 | 
			
		||||
    propnames = None                # type: Dict[int, NString]
 | 
			
		||||
    xnames = None                   # type: Dict[int, XName]
 | 
			
		||||
 | 
			
		||||
    # Data
 | 
			
		||||
    properties: list[records.Property]
 | 
			
		||||
    """Property values"""
 | 
			
		||||
    textstrings = None              # type: Dict[int, AString]
 | 
			
		||||
    propstrings = None              # type: Dict[int, AString]
 | 
			
		||||
    layers = None                   # type: List[records.LayerName]
 | 
			
		||||
 | 
			
		||||
    cells: list['Cell']
 | 
			
		||||
    """Layout cells"""
 | 
			
		||||
 | 
			
		||||
    layers: list[records.LayerName]
 | 
			
		||||
    """Layer definitions"""
 | 
			
		||||
 | 
			
		||||
    # Names
 | 
			
		||||
    cellnames: dict[int, 'CellName']
 | 
			
		||||
    """Cell names"""
 | 
			
		||||
 | 
			
		||||
    propnames: dict[int, NString]
 | 
			
		||||
    """Property names"""
 | 
			
		||||
 | 
			
		||||
    xnames: dict[int, 'XName']
 | 
			
		||||
    """Custom names"""
 | 
			
		||||
 | 
			
		||||
    # String storage
 | 
			
		||||
    textstrings: dict[int, AString]
 | 
			
		||||
    """Text strings"""
 | 
			
		||||
 | 
			
		||||
    propstrings: dict[int, AString]
 | 
			
		||||
    """Property strings"""
 | 
			
		||||
 | 
			
		||||
    def __init__(
 | 
			
		||||
            self,
 | 
			
		||||
            unit: real_t,
 | 
			
		||||
            validation: Validation | None = None,
 | 
			
		||||
            ) -> None:
 | 
			
		||||
    def __init__(self, unit: real_t, validation: Validation = None):
 | 
			
		||||
        """
 | 
			
		||||
        Args:
 | 
			
		||||
            unit: Real number (i.e. int, float, or `Fraction`), grid steps per micron.
 | 
			
		||||
            validation: `Validation` object containing checksum data.
 | 
			
		||||
                 Default creates a `Validation` object of the "no checksum" type.
 | 
			
		||||
        :param unit: Real number (i.e. int, float, or Fraction), grid steps per micron.
 | 
			
		||||
        :param validation: Validation object containing checksum data.
 | 
			
		||||
                    Default creates a Validation object of the "no checksum" type.
 | 
			
		||||
        """
 | 
			
		||||
        if validation is None:
 | 
			
		||||
            validation = Validation(0)
 | 
			
		||||
@ -118,19 +103,16 @@ class OasisLayout:
 | 
			
		||||
        self.layers = []
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def read(stream: IO[bytes]) -> 'OasisLayout':
 | 
			
		||||
    def read(stream: io.BufferedIOBase) -> 'OasisLayout':
 | 
			
		||||
        """
 | 
			
		||||
        Read an entire .oas file into an `OasisLayout` object.
 | 
			
		||||
        Read an entire .oas file into an OasisLayout object.
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            stream: Stream to read from.
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            New `OasisLayout` object.
 | 
			
		||||
        :param stream: Stream to read from.
 | 
			
		||||
        :return: New OasisLayout object.
 | 
			
		||||
        """
 | 
			
		||||
        layout = OasisLayout(unit=-1)    # dummy unit
 | 
			
		||||
        file_state = FileModals()
 | 
			
		||||
        modals = Modals()
 | 
			
		||||
        file_state = FileModals(layout.properties)
 | 
			
		||||
        layout = OasisLayout(unit=None)
 | 
			
		||||
 | 
			
		||||
        read_magic_bytes(stream)
 | 
			
		||||
 | 
			
		||||
@ -138,39 +120,32 @@ class OasisLayout:
 | 
			
		||||
            pass
 | 
			
		||||
        return layout
 | 
			
		||||
 | 
			
		||||
    def read_record(
 | 
			
		||||
            self,
 | 
			
		||||
            stream: IO[bytes],
 | 
			
		||||
    def read_record(self,
 | 
			
		||||
                    stream: io.BufferedIOBase,
 | 
			
		||||
                    modals: Modals,
 | 
			
		||||
                    file_state: FileModals
 | 
			
		||||
                    ) -> bool:
 | 
			
		||||
        """
 | 
			
		||||
        Read a single record of unspecified type from a stream, adding its
 | 
			
		||||
         contents into this `OasisLayout` object.
 | 
			
		||||
         contents into this OasisLayout object.
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            stream: Stream to read from.
 | 
			
		||||
            modals: Modal variable data, used to fill unfilled record
 | 
			
		||||
        :param stream: Stream to read from.
 | 
			
		||||
        :param modals: Modal variable data, used to fill unfilled record
 | 
			
		||||
            fields and updated using filled record fields.
 | 
			
		||||
            file_state: File status data.
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            `True` if EOF was reached without error, `False` otherwise.
 | 
			
		||||
 | 
			
		||||
        Raises:
 | 
			
		||||
            InvalidRecordError: from unexpected records
 | 
			
		||||
            InvalidDataError: from within record parsers
 | 
			
		||||
        :param file_state: File status data.
 | 
			
		||||
        :return: True if EOF was reached without error, False otherwise.
 | 
			
		||||
        :raises: InvalidRecordError from unexpected records;
 | 
			
		||||
            InvalidDataError from within record parsers.
 | 
			
		||||
        """
 | 
			
		||||
        try:
 | 
			
		||||
            record_id = read_uint(stream)
 | 
			
		||||
        except EOFError:
 | 
			
		||||
        except EOFError as e:
 | 
			
		||||
            if file_state.within_cblock:
 | 
			
		||||
                return True
 | 
			
		||||
            raise
 | 
			
		||||
            else:
 | 
			
		||||
                raise e
 | 
			
		||||
 | 
			
		||||
        logger.info(f'read_record of type {record_id} at position 0x{stream.tell():x}')
 | 
			
		||||
 | 
			
		||||
        record: Record
 | 
			
		||||
        logger.info('read_record of type {} at position 0x{:x}'.format(record_id, stream.tell()))
 | 
			
		||||
 | 
			
		||||
        # CBlock
 | 
			
		||||
        if record_id == 34:
 | 
			
		||||
@ -188,10 +163,11 @@ class OasisLayout:
 | 
			
		||||
 | 
			
		||||
        # Make sure order is valid (eg, no out-of-cell geometry)
 | 
			
		||||
        if not file_state.started and record_id != 1:
 | 
			
		||||
            raise InvalidRecordError(f'Non-Start record {record_id} before Start')
 | 
			
		||||
            raise InvalidRecordError('Non-Start record {} before Start'.format(record_id))
 | 
			
		||||
        if record_id == 1:
 | 
			
		||||
            if file_state.started:
 | 
			
		||||
                raise InvalidRecordError('Duplicate Start record')
 | 
			
		||||
            else:
 | 
			
		||||
                file_state.started = True
 | 
			
		||||
        if record_id == 2 and file_state.within_cblock:
 | 
			
		||||
            raise InvalidRecordError('End within CBlock')
 | 
			
		||||
@ -200,28 +176,25 @@ class OasisLayout:
 | 
			
		||||
            pass
 | 
			
		||||
        elif record_id in range(3, 13) or record_id in (28, 29):
 | 
			
		||||
            file_state.within_cell = False
 | 
			
		||||
        elif record_id in range(15, 28) or record_id in (32, 33):
 | 
			
		||||
        elif record_id in range(15, 29) or record_id in (32, 33):
 | 
			
		||||
            if not file_state.within_cell:
 | 
			
		||||
                raise InvalidRecordError('Geometry outside Cell')
 | 
			
		||||
                raise Exception('Geometry outside Cell')
 | 
			
		||||
        elif record_id in (13, 14):
 | 
			
		||||
            file_state.within_cell = True
 | 
			
		||||
        else:
 | 
			
		||||
            raise InvalidRecordError(f'Unknown record id: {record_id}')
 | 
			
		||||
            raise InvalidRecordError('Unknown record id: {}'.format(record_id))
 | 
			
		||||
 | 
			
		||||
        if record_id == 0:
 | 
			
		||||
            ''' Pad '''
 | 
			
		||||
            # Pad
 | 
			
		||||
            pass
 | 
			
		||||
        elif record_id == 1:
 | 
			
		||||
            ''' Start '''
 | 
			
		||||
            record = records.Start.read(stream, record_id)
 | 
			
		||||
            record.merge_with_modals(modals)
 | 
			
		||||
            self.unit = record.unit
 | 
			
		||||
            self.version = record.version
 | 
			
		||||
            file_state.end_has_offset_table = record.offset_table is None
 | 
			
		||||
            file_state.property_target = self.properties
 | 
			
		||||
            # TODO Offset table strict check
 | 
			
		||||
        elif record_id == 2:
 | 
			
		||||
            ''' End '''
 | 
			
		||||
            record = records.End.read(stream, record_id, file_state.end_has_offset_table)
 | 
			
		||||
            record.merge_with_modals(modals)
 | 
			
		||||
            self.validation = record.validation
 | 
			
		||||
@ -229,7 +202,6 @@ class OasisLayout:
 | 
			
		||||
                raise InvalidRecordError('Stream continues past End record')
 | 
			
		||||
            return True
 | 
			
		||||
        elif record_id in (3, 4):
 | 
			
		||||
            ''' CellName '''
 | 
			
		||||
            implicit = record_id == 3
 | 
			
		||||
            if file_state.cellname_implicit is None:
 | 
			
		||||
                file_state.cellname_implicit = implicit
 | 
			
		||||
@ -241,12 +213,8 @@ class OasisLayout:
 | 
			
		||||
            key = record.reference_number
 | 
			
		||||
            if key is None:
 | 
			
		||||
                key = len(self.cellnames)
 | 
			
		||||
 | 
			
		||||
            cellname = CellName.from_record(record)
 | 
			
		||||
            self.cellnames[key] = cellname
 | 
			
		||||
            file_state.property_target = cellname.properties
 | 
			
		||||
            self.cellnames[key] = record.nstring
 | 
			
		||||
        elif record_id in (5, 6):
 | 
			
		||||
            ''' TextString '''
 | 
			
		||||
            implicit = record_id == 5
 | 
			
		||||
            if file_state.textstring_implicit is None:
 | 
			
		||||
                file_state.textstring_implicit = implicit
 | 
			
		||||
@ -260,7 +228,6 @@ class OasisLayout:
 | 
			
		||||
                key = len(self.textstrings)
 | 
			
		||||
            self.textstrings[key] = record.astring
 | 
			
		||||
        elif record_id in (7, 8):
 | 
			
		||||
            ''' PropName '''
 | 
			
		||||
            implicit = record_id == 7
 | 
			
		||||
            if file_state.propname_implicit is None:
 | 
			
		||||
                file_state.propname_implicit = implicit
 | 
			
		||||
@ -274,7 +241,6 @@ class OasisLayout:
 | 
			
		||||
                key = len(self.propnames)
 | 
			
		||||
            self.propnames[key] = record.nstring
 | 
			
		||||
        elif record_id in (9, 10):
 | 
			
		||||
            ''' PropString '''
 | 
			
		||||
            implicit = record_id == 9
 | 
			
		||||
            if file_state.propstring_implicit is None:
 | 
			
		||||
                file_state.propstring_implicit = implicit
 | 
			
		||||
@ -288,17 +254,17 @@ class OasisLayout:
 | 
			
		||||
                key = len(self.propstrings)
 | 
			
		||||
            self.propstrings[key] = record.astring
 | 
			
		||||
        elif record_id in (11, 12):
 | 
			
		||||
            ''' LayerName '''
 | 
			
		||||
            record = records.LayerName.read(stream, record_id)
 | 
			
		||||
            record.merge_with_modals(modals)
 | 
			
		||||
            self.layers.append(record)
 | 
			
		||||
        elif record_id in (28, 29):
 | 
			
		||||
            ''' Property '''
 | 
			
		||||
            record = records.Property.read(stream, record_id)
 | 
			
		||||
            record.merge_with_modals(modals)
 | 
			
		||||
            file_state.property_target.append(record)
 | 
			
		||||
            if not file_state.within_cell:
 | 
			
		||||
                self.properties.append(record)
 | 
			
		||||
            else:
 | 
			
		||||
                self.cells[-1].properties.append(record)
 | 
			
		||||
        elif record_id in (30, 31):
 | 
			
		||||
            ''' XName '''
 | 
			
		||||
            implicit = record_id == 30
 | 
			
		||||
            if file_state.xname_implicit is None:
 | 
			
		||||
                file_state.xname_implicit = implicit
 | 
			
		||||
@ -311,69 +277,53 @@ class OasisLayout:
 | 
			
		||||
            if key is None:
 | 
			
		||||
                key = len(self.xnames)
 | 
			
		||||
            self.xnames[key] = XName.from_record(record)
 | 
			
		||||
            # TODO: do anything with property target?
 | 
			
		||||
 | 
			
		||||
        #
 | 
			
		||||
        # Cell and elements
 | 
			
		||||
        #
 | 
			
		||||
        elif record_id in (13, 14):
 | 
			
		||||
            ''' Cell '''
 | 
			
		||||
            record = records.Cell.read(stream, record_id)
 | 
			
		||||
            record.merge_with_modals(modals)
 | 
			
		||||
            cell = Cell(record.name)
 | 
			
		||||
            self.cells.append(cell)
 | 
			
		||||
            file_state.property_target = cell.properties
 | 
			
		||||
            self.cells.append(Cell(record.name))
 | 
			
		||||
        elif record_id in (15, 16):
 | 
			
		||||
            ''' XYMode '''
 | 
			
		||||
            record = records.XYMode.read(stream, record_id)
 | 
			
		||||
            record.merge_with_modals(modals)
 | 
			
		||||
        elif record_id in (17, 18):
 | 
			
		||||
            ''' Placement '''
 | 
			
		||||
            record = records.Placement.read(stream, record_id)
 | 
			
		||||
            record.merge_with_modals(modals)
 | 
			
		||||
            self.cells[-1].placements.append(record)
 | 
			
		||||
            file_state.property_target = record.properties
 | 
			
		||||
        elif record_id in _GEOMETRY:
 | 
			
		||||
            ''' Geometry '''
 | 
			
		||||
            record = _GEOMETRY[record_id].read(stream, record_id)
 | 
			
		||||
            record.merge_with_modals(modals)
 | 
			
		||||
            self.cells[-1].geometry.append(record)
 | 
			
		||||
            file_state.property_target = record.properties
 | 
			
		||||
        else:
 | 
			
		||||
            raise InvalidRecordError(f'Unknown record id: {record_id}')
 | 
			
		||||
            raise InvalidRecordError('Unknown record id: {}'.format(record_id))
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
    def write(self, stream: IO[bytes]) -> int:
 | 
			
		||||
    def write(self, stream: io.BufferedIOBase) -> int:
 | 
			
		||||
        """
 | 
			
		||||
        Write this object in OASIS fromat to a stream.
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            stream: Stream to write to.
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            Number of bytes written.
 | 
			
		||||
 | 
			
		||||
        Raises:
 | 
			
		||||
            InvalidDataError: if contained records are invalid.
 | 
			
		||||
        :param stream: Stream to write to.
 | 
			
		||||
        :return: Number of bytes written.
 | 
			
		||||
        :raises: InvalidDataError if contained records are invalid.
 | 
			
		||||
        """
 | 
			
		||||
        modals = Modals()
 | 
			
		||||
 | 
			
		||||
        size = 0
 | 
			
		||||
        size += write_magic_bytes(stream)
 | 
			
		||||
        size += records.Start(self.unit, self.version).dedup_write(stream, modals)
 | 
			
		||||
        size += sum(p.dedup_write(stream, modals) for p in self.properties)
 | 
			
		||||
 | 
			
		||||
        cellnames_offset = OffsetEntry(False, size)
 | 
			
		||||
        for refnum, cn in self.cellnames.items():
 | 
			
		||||
            size += records.CellName(cn.nstring, refnum).dedup_write(stream, modals)
 | 
			
		||||
            size += sum(p.dedup_write(stream, modals) for p in cn.properties)
 | 
			
		||||
        size += sum(records.CellName(name, refnum).dedup_write(stream, modals)
 | 
			
		||||
                    for refnum, name in self.cellnames.items())
 | 
			
		||||
 | 
			
		||||
        propnames_offset = OffsetEntry(False, size)
 | 
			
		||||
        size += sum(records.PropName(name, refnum).dedup_write(stream, modals)
 | 
			
		||||
                    for refnum, name in self.propnames.items())
 | 
			
		||||
 | 
			
		||||
        xnames_offset = OffsetEntry(False, size)
 | 
			
		||||
        size += sum(records.XName(x.attribute, x.bstring, refnum).dedup_write(stream, modals)
 | 
			
		||||
        size += sum(records.XName(x.attribute, x.string, refnum).dedup_write(stream, modals)
 | 
			
		||||
                    for refnum, x in self.xnames.items())
 | 
			
		||||
 | 
			
		||||
        textstrings_offset = OffsetEntry(False, size)
 | 
			
		||||
@ -387,6 +337,8 @@ class OasisLayout:
 | 
			
		||||
        layernames_offset = OffsetEntry(False, size)
 | 
			
		||||
        size += sum(r.dedup_write(stream, modals) for r in self.layers)
 | 
			
		||||
 | 
			
		||||
        size += sum(p.dedup_write(stream, modals) for p in self.properties)
 | 
			
		||||
 | 
			
		||||
        size += sum(c.dedup_write(stream, modals) for c in self.cells)
 | 
			
		||||
 | 
			
		||||
        offset_table = OffsetTable(
 | 
			
		||||
@ -404,109 +356,59 @@ class OasisLayout:
 | 
			
		||||
class Cell:
 | 
			
		||||
    """
 | 
			
		||||
    Representation of an OASIS cell.
 | 
			
		||||
 | 
			
		||||
    Properties:
 | 
			
		||||
        .name           NString or int (CellName reference number)
 | 
			
		||||
 | 
			
		||||
        .properties     List of records.Property
 | 
			
		||||
        .placements     List of records.Placement
 | 
			
		||||
        .geometry       List of geometry record objectes
 | 
			
		||||
    """
 | 
			
		||||
    name: NString | int
 | 
			
		||||
    """name or "CellName reference" number"""
 | 
			
		||||
    name = None                 # type: NString or int
 | 
			
		||||
    properties = None           # type: List[records.Property]
 | 
			
		||||
    placements = None           # type: List[records.Placement]
 | 
			
		||||
    geometry = None             # type: List[records.geometry_t]
 | 
			
		||||
 | 
			
		||||
    properties: list[records.Property]
 | 
			
		||||
    placements: list[records.Placement]
 | 
			
		||||
    geometry: list[records.geometry_t]
 | 
			
		||||
    def __init__(self, name: NString or int):
 | 
			
		||||
        """
 | 
			
		||||
        :param name: NString or int (CellName reference number)
 | 
			
		||||
        """
 | 
			
		||||
        self.name = name
 | 
			
		||||
        self.properties = []
 | 
			
		||||
        self.placements = []
 | 
			
		||||
        self.geometry = []
 | 
			
		||||
 | 
			
		||||
    def __init__(
 | 
			
		||||
            self,
 | 
			
		||||
            name: NString | str | int,
 | 
			
		||||
            *,
 | 
			
		||||
            properties: list[records.Property] | None = None,
 | 
			
		||||
            placements: list[records.Placement] | None = None,
 | 
			
		||||
            geometry: list[records.geometry_t] | None = None,
 | 
			
		||||
            ) -> None:
 | 
			
		||||
        self.name = name if isinstance(name, NString | int) else NString(name)
 | 
			
		||||
        self.properties = [] if properties is None else properties
 | 
			
		||||
        self.placements = [] if placements is None else placements
 | 
			
		||||
        self.geometry = [] if geometry is None else geometry
 | 
			
		||||
 | 
			
		||||
    def dedup_write(self, stream: IO[bytes], modals: Modals) -> int:
 | 
			
		||||
    def dedup_write(self, stream: io.BufferedIOBase, modals: Modals) -> int:
 | 
			
		||||
        """
 | 
			
		||||
        Write this cell to a stream, using the provided modal variables to
 | 
			
		||||
         deduplicate any repeated data.
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            stream: Stream to write to.
 | 
			
		||||
            modals: Modal variables to use for deduplication.
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            Number of bytes written.
 | 
			
		||||
 | 
			
		||||
        Raises:
 | 
			
		||||
            InvalidDataError: if contained records are invalid.
 | 
			
		||||
        :param stream: Stream to write to.
 | 
			
		||||
        :param modals: Modal variables to use for deduplication.
 | 
			
		||||
        :return: Number of bytes written.
 | 
			
		||||
        :raises: InvalidDataError if contained records are invalid.
 | 
			
		||||
        """
 | 
			
		||||
        size = records.Cell(self.name).dedup_write(stream, modals)
 | 
			
		||||
        size += sum(p.dedup_write(stream, modals) for p in self.properties)
 | 
			
		||||
        for placement in self.placements:
 | 
			
		||||
            size += placement.dedup_write(stream, modals)
 | 
			
		||||
            size += sum(p.dedup_write(stream, modals) for p in placement.properties)
 | 
			
		||||
        for shape in self.geometry:
 | 
			
		||||
            size += shape.dedup_write(stream, modals)
 | 
			
		||||
            size += sum(p.dedup_write(stream, modals) for p in shape.properties)
 | 
			
		||||
        size += sum(p.dedup_write(stream, modals) for p in self.placements)
 | 
			
		||||
        size += sum(g.dedup_write(stream, modals) for g in self.geometry)
 | 
			
		||||
        return size
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CellName:
 | 
			
		||||
    """
 | 
			
		||||
    Representation of a CellName.
 | 
			
		||||
 | 
			
		||||
    This class is effectively a simplified form of a `records.CellName`,
 | 
			
		||||
     with the reference data stripped out.
 | 
			
		||||
    """
 | 
			
		||||
    nstring: NString
 | 
			
		||||
    properties: list[records.Property]
 | 
			
		||||
 | 
			
		||||
    def __init__(
 | 
			
		||||
            self,
 | 
			
		||||
            nstring: NString | str,
 | 
			
		||||
            properties: list[records.Property] | None = None,
 | 
			
		||||
            ) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Args:
 | 
			
		||||
            nstring: The contained string.
 | 
			
		||||
            properties: Properties which apply to this CellName's cell, but
 | 
			
		||||
                    are placed following the CellName record.
 | 
			
		||||
        """
 | 
			
		||||
        if isinstance(nstring, NString):
 | 
			
		||||
            self.nstring = nstring
 | 
			
		||||
        else:
 | 
			
		||||
            self.nstring = NString(nstring)
 | 
			
		||||
        self.properties = [] if properties is None else properties
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def from_record(record: records.CellName) -> 'CellName':
 | 
			
		||||
        """
 | 
			
		||||
        Create an `CellName` object from a `records.CellName` record.
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            record: CellName record to use.
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            A new `CellName` object.
 | 
			
		||||
        """
 | 
			
		||||
        return CellName(record.nstring)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class XName:
 | 
			
		||||
    """
 | 
			
		||||
    Representation of an XName.
 | 
			
		||||
 | 
			
		||||
    This class is effectively a simplified form of a `records.XName`,
 | 
			
		||||
    This class is effectively a simplified form of a records.XName,
 | 
			
		||||
     with the reference data stripped out.
 | 
			
		||||
    """
 | 
			
		||||
    attribute: int
 | 
			
		||||
    bstring: bytes
 | 
			
		||||
    attribute = None        # type: int
 | 
			
		||||
    bstring = None          # type: bytes
 | 
			
		||||
 | 
			
		||||
    def __init__(self, attribute: int, bstring: bytes) -> None:
 | 
			
		||||
    def __init__(self, attribute: int, bstring: bytes):
 | 
			
		||||
        """
 | 
			
		||||
        Args:
 | 
			
		||||
            attribute: Attribute number.
 | 
			
		||||
            bstring: Binary data.
 | 
			
		||||
        :param attribute: Attribute number.
 | 
			
		||||
        :param bstring: Binary data.
 | 
			
		||||
        """
 | 
			
		||||
        self.attribute = attribute
 | 
			
		||||
        self.bstring = bstring
 | 
			
		||||
@ -514,19 +416,16 @@ class XName:
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def from_record(record: records.XName) -> 'XName':
 | 
			
		||||
        """
 | 
			
		||||
        Create an `XName` object from a `records.XName` record.
 | 
			
		||||
        Create an XName object from a records.XName record.
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            record: XName record to use.
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            a new `XName` object.
 | 
			
		||||
        :param record: XName record to use.
 | 
			
		||||
        :return: XName object.
 | 
			
		||||
        """
 | 
			
		||||
        return XName(record.attribute, record.bstring)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Mapping from record id to record class.
 | 
			
		||||
_GEOMETRY: dict[int, type[records.geometry_t]] = {
 | 
			
		||||
_GEOMETRY = {
 | 
			
		||||
    19: records.Text,
 | 
			
		||||
    20: records.Rectangle,
 | 
			
		||||
    21: records.Polygon,
 | 
			
		||||
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -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.
 | 
			
		||||
"""
 | 
			
		||||
@ -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()
 | 
			
		||||
@ -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],
 | 
			
		||||
        ])
 | 
			
		||||
@ -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
 | 
			
		||||
@ -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
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
@ -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)
 | 
			
		||||
@ -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
 | 
			
		||||
@ -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)
 | 
			
		||||
@ -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
 | 
			
		||||
@ -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
											
										
									
								
							@ -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
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
@ -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
 | 
			
		||||
@ -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
 | 
			
		||||
@ -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()
 | 
			
		||||
 | 
			
		||||
@ -1,90 +0,0 @@
 | 
			
		||||
[build-system]
 | 
			
		||||
requires = ["hatchling"]
 | 
			
		||||
build-backend = "hatchling.build"
 | 
			
		||||
 | 
			
		||||
[project]
 | 
			
		||||
name = "fatamorgana"
 | 
			
		||||
description = "OASIS layout format parser and writer"
 | 
			
		||||
readme = "README.md"
 | 
			
		||||
license = { file = "LICENSE.md" }
 | 
			
		||||
authors = [
 | 
			
		||||
    { name="Jan Petykiewicz", email="jan@mpxd.net" },
 | 
			
		||||
    ]
 | 
			
		||||
homepage = "https://mpxd.net/code/jan/fatamorgana"
 | 
			
		||||
repository = "https://mpxd.net/code/jan/fatamorgana"
 | 
			
		||||
keywords = [
 | 
			
		||||
    "OASIS",
 | 
			
		||||
    "layout",
 | 
			
		||||
    "design",
 | 
			
		||||
    "CAD",
 | 
			
		||||
    "EDA",
 | 
			
		||||
    "oas",
 | 
			
		||||
    "electronics",
 | 
			
		||||
    "open",
 | 
			
		||||
    "artwork",
 | 
			
		||||
    "interchange",
 | 
			
		||||
    "standard",
 | 
			
		||||
    "mask",
 | 
			
		||||
    "pattern",
 | 
			
		||||
    "IC",
 | 
			
		||||
    "geometry",
 | 
			
		||||
    "geometric",
 | 
			
		||||
    "polygon",
 | 
			
		||||
    "gds",
 | 
			
		||||
    ]
 | 
			
		||||
classifiers = [
 | 
			
		||||
    "Programming Language :: Python :: 3",
 | 
			
		||||
    "Development Status :: 3 - Alpha",
 | 
			
		||||
    "Environment :: Other Environment",
 | 
			
		||||
    "Intended Audience :: Developers",
 | 
			
		||||
    "Intended Audience :: Information Technology",
 | 
			
		||||
    "Intended Audience :: Manufacturing",
 | 
			
		||||
    "Intended Audience :: Science/Research",
 | 
			
		||||
    "License :: OSI Approved :: GNU Affero General Public License v3",
 | 
			
		||||
    "Topic :: Scientific/Engineering",
 | 
			
		||||
    "Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)",
 | 
			
		||||
    ]
 | 
			
		||||
requires-python = ">=3.11"
 | 
			
		||||
dynamic = ["version"]
 | 
			
		||||
dependencies = [
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
[tool.hatch.version]
 | 
			
		||||
path = "fatamorgana/__init__.py"
 | 
			
		||||
 | 
			
		||||
[project.optional-dependencies]
 | 
			
		||||
numpy = ["numpy>=1.26"]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
[tool.ruff]
 | 
			
		||||
exclude = [
 | 
			
		||||
    ".git",
 | 
			
		||||
    "dist",
 | 
			
		||||
    ]
 | 
			
		||||
line-length = 145
 | 
			
		||||
indent-width = 4
 | 
			
		||||
lint.dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
 | 
			
		||||
lint.select = [
 | 
			
		||||
    "NPY", "E", "F", "W", "B", "ANN", "UP", "SLOT", "SIM", "LOG",
 | 
			
		||||
    "C4", "ISC", "PIE", "PT", "RET", "TCH", "PTH", "INT",
 | 
			
		||||
    "ARG", "PL", "R", "TRY",
 | 
			
		||||
    "G010", "G101", "G201", "G202",
 | 
			
		||||
    "Q002", "Q003", "Q004",
 | 
			
		||||
    ]
 | 
			
		||||
lint.ignore = [
 | 
			
		||||
    #"ANN001",   # No annotation
 | 
			
		||||
    "ANN002",   # *args
 | 
			
		||||
    "ANN003",   # **kwargs
 | 
			
		||||
    "ANN401",   # Any
 | 
			
		||||
    "ANN101",   # self: Self
 | 
			
		||||
    "SIM108",   # single-line if / else assignment
 | 
			
		||||
    "RET504",   # x=y+z; return x
 | 
			
		||||
    "PIE790",   # unnecessary pass
 | 
			
		||||
    "ISC003",   # non-implicit string concatenation
 | 
			
		||||
    "C408",     # dict(x=y) instead of {'x': y}
 | 
			
		||||
    "PLR09",    # Too many xxx
 | 
			
		||||
    "PLR2004",  # magic number
 | 
			
		||||
    "PLC0414",  # import x as x
 | 
			
		||||
    "TRY003",   # Long exception message
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										61
									
								
								setup.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								setup.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,61 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
 | 
			
		||||
from setuptools import setup, find_packages
 | 
			
		||||
import fatamorgana
 | 
			
		||||
 | 
			
		||||
with open('README.md', 'r') as f:
 | 
			
		||||
    long_description = f.read()
 | 
			
		||||
 | 
			
		||||
setup(name='fatamorgana',
 | 
			
		||||
      version=fatamorgana.version,
 | 
			
		||||
      description='OASIS layout format parser and writer',
 | 
			
		||||
      long_description=long_description,
 | 
			
		||||
      long_description_content_type='text/markdown',
 | 
			
		||||
      author='Jan Petykiewicz',
 | 
			
		||||
      author_email='anewusername@gmail.com',
 | 
			
		||||
      url='https://mpxd.net/code/jan/fatamorgana',
 | 
			
		||||
      keywords=[
 | 
			
		||||
          'OASIS',
 | 
			
		||||
          'layout',
 | 
			
		||||
          'design',
 | 
			
		||||
          'CAD',
 | 
			
		||||
          'EDA',
 | 
			
		||||
          'oas',
 | 
			
		||||
          'electronics',
 | 
			
		||||
          'open',
 | 
			
		||||
          'artwork',
 | 
			
		||||
          'interchange',
 | 
			
		||||
          'standard',
 | 
			
		||||
          'mask',
 | 
			
		||||
          'pattern',
 | 
			
		||||
          'IC',
 | 
			
		||||
          'geometry',
 | 
			
		||||
          'geometric',
 | 
			
		||||
          'polygon',
 | 
			
		||||
          'gds',
 | 
			
		||||
      ],
 | 
			
		||||
      classifiers=[
 | 
			
		||||
            'Programming Language :: Python',
 | 
			
		||||
            'Programming Language :: Python :: 3',
 | 
			
		||||
            'Development Status :: 3 - Alpha',
 | 
			
		||||
            'Environment :: Other Environment',
 | 
			
		||||
            'Intended Audience :: Developers',
 | 
			
		||||
            'Intended Audience :: Information Technology',
 | 
			
		||||
            'Intended Audience :: Manufacturing',
 | 
			
		||||
            'Intended Audience :: Science/Research',
 | 
			
		||||
            'License :: OSI Approved :: GNU Affero General Public License v3',
 | 
			
		||||
            'Operating System :: POSIX :: Linux',
 | 
			
		||||
            'Operating System :: Microsoft :: Windows',
 | 
			
		||||
            'Topic :: Scientific/Engineering',
 | 
			
		||||
            'Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)',
 | 
			
		||||
            'Topic :: Software Development :: Libraries :: Python Modules',
 | 
			
		||||
      ],
 | 
			
		||||
      packages=find_packages(),
 | 
			
		||||
      install_requires=[
 | 
			
		||||
            'typing',
 | 
			
		||||
      ],
 | 
			
		||||
      extras_require={
 | 
			
		||||
          'numpy': ['numpy'],
 | 
			
		||||
      },
 | 
			
		||||
      )
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user