Add UnfilledModalError, records.verify_modal(), and .get_*() methods.
The .get_*() methods are used to verify that we aren't reading from a pattern with un-filled modals. The GeometryMixin class was also added here and provides some additional convenience methods: get_xy() to get an (x,y) tuple and get_layer_tuple() to get a (layer, datatype) tuple.
This commit is contained in:
parent
e4a62a0f32
commit
705926d443
@ -29,6 +29,7 @@ import pathlib
|
|||||||
from .main import OasisLayout, Cell, XName
|
from .main import OasisLayout, Cell, XName
|
||||||
from .basic import NString, AString, Validation, OffsetTable, OffsetEntry, \
|
from .basic import NString, AString, Validation, OffsetTable, OffsetEntry, \
|
||||||
EOFError, SignedError, InvalidDataError, InvalidRecordError, \
|
EOFError, SignedError, InvalidDataError, InvalidRecordError, \
|
||||||
|
UnfilledModalError, \
|
||||||
ReuseRepetition, GridRepetition, ArbitraryRepetition
|
ReuseRepetition, GridRepetition, ArbitraryRepetition
|
||||||
|
|
||||||
|
|
||||||
|
@ -59,6 +59,12 @@ class InvalidRecordError(FatamorganaError):
|
|||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
class UnfilledModalError(FatamorganaError):
|
||||||
|
"""
|
||||||
|
Attempted to call .get_var(), but var() was None!
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class PathExtensionScheme(Enum):
|
class PathExtensionScheme(Enum):
|
||||||
"""
|
"""
|
||||||
@ -2228,4 +2234,3 @@ def read_magic_bytes(stream: io.BufferedIOBase):
|
|||||||
if magic != MAGIC_BYTES:
|
if magic != MAGIC_BYTES:
|
||||||
raise InvalidDataError('Could not read magic bytes, '
|
raise InvalidDataError('Could not read magic bytes, '
|
||||||
'found {!r} : {}'.format(magic, magic.decode()))
|
'found {!r} : {}'.format(magic, magic.decode()))
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ Higher-level code (e.g. monitoring for combinations of records with
|
|||||||
in main.py instead.
|
in main.py instead.
|
||||||
"""
|
"""
|
||||||
from abc import ABCMeta, abstractmethod
|
from abc import ABCMeta, abstractmethod
|
||||||
from typing import List, Dict, Tuple, Union, Optional, Sequence, Any
|
from typing import List, Dict, Tuple, Union, Optional, Sequence, Any, TypeVar
|
||||||
import copy
|
import copy
|
||||||
import math
|
import math
|
||||||
import zlib
|
import zlib
|
||||||
@ -24,7 +24,7 @@ from .basic import AString, NString, repetition_t, property_value_t, real_t, \
|
|||||||
read_bstring, read_uint, read_sint, read_real, read_repetition, read_interval, \
|
read_bstring, read_uint, read_sint, read_real, read_repetition, read_interval, \
|
||||||
write_bstring, write_uint, write_sint, write_real, write_interval, write_point_list, \
|
write_bstring, write_uint, write_sint, write_real, write_interval, write_point_list, \
|
||||||
write_property_value, read_bool_byte, write_bool_byte, read_byte, write_byte, \
|
write_property_value, read_bool_byte, write_bool_byte, read_byte, write_byte, \
|
||||||
InvalidDataError, PathExtensionScheme, _USE_NUMPY
|
InvalidDataError, UnfilledModalError, PathExtensionScheme, _USE_NUMPY
|
||||||
|
|
||||||
if _USE_NUMPY:
|
if _USE_NUMPY:
|
||||||
import numpy
|
import numpy
|
||||||
@ -32,6 +32,7 @@ if _USE_NUMPY:
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
Type definitions
|
Type definitions
|
||||||
'''
|
'''
|
||||||
@ -112,6 +113,12 @@ class Modals:
|
|||||||
self.property_is_standard = None
|
self.property_is_standard = None
|
||||||
|
|
||||||
|
|
||||||
|
T = TypeVar('T')
|
||||||
|
def verify_modal(var: Optional[T]) -> T:
|
||||||
|
if var is None:
|
||||||
|
raise UnfilledModalError
|
||||||
|
return var
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
Records
|
Records
|
||||||
@ -217,6 +224,34 @@ class Record(metaclass=ABCMeta):
|
|||||||
return '{}: {}'.format(self.__class__, pprint.pformat(self.__dict__))
|
return '{}: {}'.format(self.__class__, pprint.pformat(self.__dict__))
|
||||||
|
|
||||||
|
|
||||||
|
class GeometryMixin(metaclass=ABCMeta):
|
||||||
|
"""
|
||||||
|
Mixin defining common functions for geometry records
|
||||||
|
"""
|
||||||
|
x: Optional[int]
|
||||||
|
y: Optional[int]
|
||||||
|
layer: Optional[int]
|
||||||
|
datatype: Optional[int]
|
||||||
|
|
||||||
|
def get_x(self) -> int:
|
||||||
|
return verify_modal(self.x)
|
||||||
|
|
||||||
|
def get_y(self) -> int:
|
||||||
|
return verify_modal(self.y)
|
||||||
|
|
||||||
|
def get_xy(self) -> Tuple[int, int]:
|
||||||
|
return (self.get_x(), self.get_y())
|
||||||
|
|
||||||
|
def get_layer(self) -> int:
|
||||||
|
return verify_modal(self.layer)
|
||||||
|
|
||||||
|
def get_datatype(self) -> int:
|
||||||
|
return verify_modal(self.datatype)
|
||||||
|
|
||||||
|
def get_layer_tuple(self) -> Tuple[int, int]:
|
||||||
|
return (self.get_layer(), self.get_datatype())
|
||||||
|
|
||||||
|
|
||||||
def read_refname(stream: io.BufferedIOBase,
|
def read_refname(stream: io.BufferedIOBase,
|
||||||
is_present: Union[bool, int],
|
is_present: Union[bool, int],
|
||||||
is_reference: Union[bool, int]
|
is_reference: Union[bool, int]
|
||||||
@ -910,6 +945,15 @@ class Property(Record):
|
|||||||
self.values = values
|
self.values = values
|
||||||
self.is_standard = is_standard
|
self.is_standard = is_standard
|
||||||
|
|
||||||
|
def get_name(self) -> Union[NString, int]:
|
||||||
|
return verify_modal(self.name) # type: ignore
|
||||||
|
|
||||||
|
def get_values(self) -> List[property_value_t]:
|
||||||
|
return verify_modal(self.values)
|
||||||
|
|
||||||
|
def get_is_standard(self) -> bool:
|
||||||
|
return verify_modal(self.is_standard)
|
||||||
|
|
||||||
def merge_with_modals(self, modals: Modals):
|
def merge_with_modals(self, modals: Modals):
|
||||||
adjust_field(self, 'name', modals, 'property_name')
|
adjust_field(self, 'name', modals, 'property_name')
|
||||||
adjust_field(self, 'values', modals, 'property_value_list')
|
adjust_field(self, 'values', modals, 'property_value_list')
|
||||||
@ -1091,7 +1135,7 @@ class XElement(Record):
|
|||||||
return size
|
return size
|
||||||
|
|
||||||
|
|
||||||
class XGeometry(Record):
|
class XGeometry(Record, GeometryMixin):
|
||||||
"""
|
"""
|
||||||
XGeometry record (ID 33)
|
XGeometry record (ID 33)
|
||||||
|
|
||||||
@ -1301,6 +1345,15 @@ class Placement(Record):
|
|||||||
else:
|
else:
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|
||||||
|
def get_name(self) -> Union[NString, int]:
|
||||||
|
return verify_modal(self.name) # type: ignore
|
||||||
|
|
||||||
|
def get_x(self) -> int:
|
||||||
|
return verify_modal(self.x)
|
||||||
|
|
||||||
|
def get_y(self) -> int:
|
||||||
|
return verify_modal(self.y)
|
||||||
|
|
||||||
def merge_with_modals(self, modals: Modals):
|
def merge_with_modals(self, modals: Modals):
|
||||||
adjust_coordinates(self, modals, 'placement_x', 'placement_y')
|
adjust_coordinates(self, modals, 'placement_x', 'placement_y')
|
||||||
adjust_repetition(self, modals)
|
adjust_repetition(self, modals)
|
||||||
@ -1384,7 +1437,7 @@ class Placement(Record):
|
|||||||
return size
|
return size
|
||||||
|
|
||||||
|
|
||||||
class Text(Record):
|
class Text(Record, GeometryMixin):
|
||||||
"""
|
"""
|
||||||
Text record (ID 19)
|
Text record (ID 19)
|
||||||
|
|
||||||
@ -1430,6 +1483,9 @@ class Text(Record):
|
|||||||
else:
|
else:
|
||||||
self.string = string
|
self.string = string
|
||||||
|
|
||||||
|
def get_string(self) -> Union[AString, int]:
|
||||||
|
return verify_modal(self.string) # type: ignore
|
||||||
|
|
||||||
def merge_with_modals(self, modals: Modals):
|
def merge_with_modals(self, modals: Modals):
|
||||||
adjust_coordinates(self, modals, 'text_x', 'text_y')
|
adjust_coordinates(self, modals, 'text_x', 'text_y')
|
||||||
adjust_repetition(self, modals)
|
adjust_repetition(self, modals)
|
||||||
@ -1500,7 +1556,7 @@ class Text(Record):
|
|||||||
return size
|
return size
|
||||||
|
|
||||||
|
|
||||||
class Rectangle(Record):
|
class Rectangle(Record, GeometryMixin):
|
||||||
"""
|
"""
|
||||||
Rectangle record (ID 20)
|
Rectangle record (ID 20)
|
||||||
|
|
||||||
@ -1558,6 +1614,14 @@ class Rectangle(Record):
|
|||||||
if is_square and self.height is not None:
|
if is_square and self.height is not None:
|
||||||
raise InvalidDataError('Rectangle is square and also has height')
|
raise InvalidDataError('Rectangle is square and also has height')
|
||||||
|
|
||||||
|
def get_width(self) -> int:
|
||||||
|
return verify_modal(self.width)
|
||||||
|
|
||||||
|
def get_height(self) -> int:
|
||||||
|
if self.is_square:
|
||||||
|
return verify_modal(self.width)
|
||||||
|
return verify_modal(self.height)
|
||||||
|
|
||||||
def merge_with_modals(self, modals: Modals):
|
def merge_with_modals(self, modals: Modals):
|
||||||
adjust_coordinates(self, modals, 'geometry_x', 'geometry_y')
|
adjust_coordinates(self, modals, 'geometry_x', 'geometry_y')
|
||||||
adjust_repetition(self, modals)
|
adjust_repetition(self, modals)
|
||||||
@ -1635,7 +1699,7 @@ class Rectangle(Record):
|
|||||||
return size
|
return size
|
||||||
|
|
||||||
|
|
||||||
class Polygon(Record):
|
class Polygon(Record, GeometryMixin):
|
||||||
"""
|
"""
|
||||||
Polygon record (ID 21)
|
Polygon record (ID 21)
|
||||||
|
|
||||||
@ -1685,6 +1749,9 @@ class Polygon(Record):
|
|||||||
if len(point_list) < 3:
|
if len(point_list) < 3:
|
||||||
warn('Polygon with < 3 points')
|
warn('Polygon with < 3 points')
|
||||||
|
|
||||||
|
def get_point_list(self) -> point_list_t:
|
||||||
|
return verify_modal(self.point_list)
|
||||||
|
|
||||||
def merge_with_modals(self, modals: Modals):
|
def merge_with_modals(self, modals: Modals):
|
||||||
adjust_coordinates(self, modals, 'geometry_x', 'geometry_y')
|
adjust_coordinates(self, modals, 'geometry_x', 'geometry_y')
|
||||||
adjust_repetition(self, modals)
|
adjust_repetition(self, modals)
|
||||||
@ -1752,7 +1819,7 @@ class Polygon(Record):
|
|||||||
return size
|
return size
|
||||||
|
|
||||||
|
|
||||||
class Path(Record):
|
class Path(Record, GeometryMixin):
|
||||||
"""
|
"""
|
||||||
Polygon record (ID 22)
|
Polygon record (ID 22)
|
||||||
|
|
||||||
@ -1821,6 +1888,18 @@ class Path(Record):
|
|||||||
self.extension_start = extension_start
|
self.extension_start = extension_start
|
||||||
self.extension_end = extension_end
|
self.extension_end = extension_end
|
||||||
|
|
||||||
|
def get_point_list(self) -> point_list_t:
|
||||||
|
return verify_modal(self.point_list)
|
||||||
|
|
||||||
|
def get_half_width(self) -> int:
|
||||||
|
return verify_modal(self.half_width)
|
||||||
|
|
||||||
|
def get_extension_start(self) -> pathextension_t:
|
||||||
|
return verify_modal(self.extension_start)
|
||||||
|
|
||||||
|
def get_extension_end(self) -> pathextension_t:
|
||||||
|
return verify_modal(self.extension_end)
|
||||||
|
|
||||||
def merge_with_modals(self, modals: Modals):
|
def merge_with_modals(self, modals: Modals):
|
||||||
adjust_coordinates(self, modals, 'geometry_x', 'geometry_y')
|
adjust_coordinates(self, modals, 'geometry_x', 'geometry_y')
|
||||||
adjust_repetition(self, modals)
|
adjust_repetition(self, modals)
|
||||||
@ -1927,7 +2006,7 @@ class Path(Record):
|
|||||||
return size
|
return size
|
||||||
|
|
||||||
|
|
||||||
class Trapezoid(Record):
|
class Trapezoid(Record, GeometryMixin):
|
||||||
"""
|
"""
|
||||||
Trapezoid record (ID 23, 24, 25)
|
Trapezoid record (ID 23, 24, 25)
|
||||||
|
|
||||||
@ -2017,6 +2096,21 @@ class Trapezoid(Record):
|
|||||||
raise InvalidDataError('Trapezoid: w < delta_b - delta_a'
|
raise InvalidDataError('Trapezoid: w < delta_b - delta_a'
|
||||||
' ({} < {} - {})'.format(width, delta_b, delta_a))
|
' ({} < {} - {})'.format(width, delta_b, delta_a))
|
||||||
|
|
||||||
|
def get_is_vertical(self) -> bool:
|
||||||
|
return verify_modal(self.is_vertical)
|
||||||
|
|
||||||
|
def get_delta_a(self) -> int:
|
||||||
|
return verify_modal(self.delta_a)
|
||||||
|
|
||||||
|
def get_delta_b(self) -> int:
|
||||||
|
return verify_modal(self.delta_b)
|
||||||
|
|
||||||
|
def get_width(self) -> int:
|
||||||
|
return verify_modal(self.width)
|
||||||
|
|
||||||
|
def get_height(self) -> int:
|
||||||
|
return verify_modal(self.height)
|
||||||
|
|
||||||
def merge_with_modals(self, modals: Modals):
|
def merge_with_modals(self, modals: Modals):
|
||||||
adjust_coordinates(self, modals, 'geometry_x', 'geometry_y')
|
adjust_coordinates(self, modals, 'geometry_x', 'geometry_y')
|
||||||
adjust_repetition(self, modals)
|
adjust_repetition(self, modals)
|
||||||
@ -2103,7 +2197,7 @@ class Trapezoid(Record):
|
|||||||
|
|
||||||
|
|
||||||
# TODO: CTrapezoid type descriptions
|
# TODO: CTrapezoid type descriptions
|
||||||
class CTrapezoid(Record):
|
class CTrapezoid(Record, GeometryMixin):
|
||||||
"""
|
"""
|
||||||
CTrapezoid record (ID 26)
|
CTrapezoid record (ID 26)
|
||||||
|
|
||||||
@ -2161,6 +2255,23 @@ class CTrapezoid(Record):
|
|||||||
|
|
||||||
self.check_valid()
|
self.check_valid()
|
||||||
|
|
||||||
|
def get_ctrapezoid_type(self) -> int:
|
||||||
|
return verify_modal(self.ctrapezoid_type)
|
||||||
|
|
||||||
|
def get_height(self) -> int:
|
||||||
|
if self.ctrapezoid_type is None:
|
||||||
|
return verify_modal(self.height)
|
||||||
|
if self.ctrapezoid_type in (16, 17, 18, 19, 22, 23, 25):
|
||||||
|
return verify_modal(self.width)
|
||||||
|
return verify_modal(self.height)
|
||||||
|
|
||||||
|
def get_width(self) -> int:
|
||||||
|
if self.ctrapezoid_type is None:
|
||||||
|
return verify_modal(self.width)
|
||||||
|
if self.ctrapezoid_type in (20, 21):
|
||||||
|
return verify_modal(self.height)
|
||||||
|
return verify_modal(self.width)
|
||||||
|
|
||||||
def merge_with_modals(self, modals: Modals):
|
def merge_with_modals(self, modals: Modals):
|
||||||
adjust_coordinates(self, modals, 'geometry_x', 'geometry_y')
|
adjust_coordinates(self, modals, 'geometry_x', 'geometry_y')
|
||||||
adjust_repetition(self, modals)
|
adjust_repetition(self, modals)
|
||||||
@ -2267,7 +2378,6 @@ class CTrapezoid(Record):
|
|||||||
size += self.repetition.write(stream) # type: ignore
|
size += self.repetition.write(stream) # type: ignore
|
||||||
return size
|
return size
|
||||||
|
|
||||||
|
|
||||||
def check_valid(self):
|
def check_valid(self):
|
||||||
ctrapezoid_type = self.ctrapezoid_type
|
ctrapezoid_type = self.ctrapezoid_type
|
||||||
width = self.width
|
width = self.width
|
||||||
@ -2299,7 +2409,7 @@ class CTrapezoid(Record):
|
|||||||
'{}'.format(ctrapezoid_type))
|
'{}'.format(ctrapezoid_type))
|
||||||
|
|
||||||
|
|
||||||
class Circle(Record):
|
class Circle(Record, GeometryMixin):
|
||||||
"""
|
"""
|
||||||
Circle record (ID 27)
|
Circle record (ID 27)
|
||||||
|
|
||||||
@ -2344,6 +2454,9 @@ class Circle(Record):
|
|||||||
self.y = y
|
self.y = y
|
||||||
self.repetition = repetition
|
self.repetition = repetition
|
||||||
|
|
||||||
|
def get_radius(self) -> int:
|
||||||
|
return verify_modal(self.radius)
|
||||||
|
|
||||||
def merge_with_modals(self, modals: Modals):
|
def merge_with_modals(self, modals: Modals):
|
||||||
adjust_coordinates(self, modals, 'geometry_x', 'geometry_y')
|
adjust_coordinates(self, modals, 'geometry_x', 'geometry_y')
|
||||||
adjust_repetition(self, modals)
|
adjust_repetition(self, modals)
|
||||||
|
Loading…
Reference in New Issue
Block a user