You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
451 lines
16 KiB
Python
451 lines
16 KiB
Python
# type: ignore
|
|
|
|
from typing import List, Tuple, Iterable
|
|
from itertools import chain
|
|
from io import BytesIO, BufferedIOBase
|
|
import struct
|
|
|
|
import pytest # type: ignore
|
|
import numpy
|
|
from numpy.testing import assert_equal
|
|
|
|
from .utils import HEADER, FOOTER
|
|
from ..basic import write_uint, write_sint, read_uint, read_sint, write_bstring, write_byte
|
|
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.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: BufferedIOBase, variant: int) -> BufferedIOBase:
|
|
'''
|
|
'''
|
|
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: BufferedIOBase) -> BufferedIOBase:
|
|
'''
|
|
'''
|
|
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
|
|
assert len(gg.properties[0].values) == 1, msg
|
|
assert gg.properties[0].values[0] * 5 == 1, msg
|
|
|