forked from jan/fatamorgana
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 .basic import NString, AString, Validation, OffsetTable, OffsetEntry, \
|
||||
EOFError, SignedError, InvalidDataError, InvalidRecordError, \
|
||||
UnfilledModalError, \
|
||||
ReuseRepetition, GridRepetition, ArbitraryRepetition
|
||||
|
||||
|
||||
|
@ -59,6 +59,12 @@ class InvalidRecordError(FatamorganaError):
|
||||
"""
|
||||
pass
|
||||
|
||||
class UnfilledModalError(FatamorganaError):
|
||||
"""
|
||||
Attempted to call .get_var(), but var() was None!
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class PathExtensionScheme(Enum):
|
||||
"""
|
||||
@ -2228,4 +2234,3 @@ def read_magic_bytes(stream: io.BufferedIOBase):
|
||||
if magic != MAGIC_BYTES:
|
||||
raise InvalidDataError('Could not read magic bytes, '
|
||||
'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.
|
||||
"""
|
||||
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 math
|
||||
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, \
|
||||
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, \
|
||||
InvalidDataError, PathExtensionScheme, _USE_NUMPY
|
||||
InvalidDataError, UnfilledModalError, PathExtensionScheme, _USE_NUMPY
|
||||
|
||||
if _USE_NUMPY:
|
||||
import numpy
|
||||
@ -32,6 +32,7 @@ if _USE_NUMPY:
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
'''
|
||||
Type definitions
|
||||
'''
|
||||
@ -112,6 +113,12 @@ class Modals:
|
||||
self.property_is_standard = None
|
||||
|
||||
|
||||
T = TypeVar('T')
|
||||
def verify_modal(var: Optional[T]) -> T:
|
||||
if var is None:
|
||||
raise UnfilledModalError
|
||||
return var
|
||||
|
||||
'''
|
||||
|
||||
Records
|
||||
@ -217,6 +224,34 @@ class Record(metaclass=ABCMeta):
|
||||
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,
|
||||
is_present: Union[bool, int],
|
||||
is_reference: Union[bool, int]
|
||||
@ -910,6 +945,15 @@ class Property(Record):
|
||||
self.values = values
|
||||
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):
|
||||
adjust_field(self, 'name', modals, 'property_name')
|
||||
adjust_field(self, 'values', modals, 'property_value_list')
|
||||
@ -1091,7 +1135,7 @@ class XElement(Record):
|
||||
return size
|
||||
|
||||
|
||||
class XGeometry(Record):
|
||||
class XGeometry(Record, GeometryMixin):
|
||||
"""
|
||||
XGeometry record (ID 33)
|
||||
|
||||
@ -1301,6 +1345,15 @@ class Placement(Record):
|
||||
else:
|
||||
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):
|
||||
adjust_coordinates(self, modals, 'placement_x', 'placement_y')
|
||||
adjust_repetition(self, modals)
|
||||
@ -1384,7 +1437,7 @@ class Placement(Record):
|
||||
return size
|
||||
|
||||
|
||||
class Text(Record):
|
||||
class Text(Record, GeometryMixin):
|
||||
"""
|
||||
Text record (ID 19)
|
||||
|
||||
@ -1430,6 +1483,9 @@ class Text(Record):
|
||||
else:
|
||||
self.string = string
|
||||
|
||||
def get_string(self) -> Union[AString, int]:
|
||||
return verify_modal(self.string) # type: ignore
|
||||
|
||||
def merge_with_modals(self, modals: Modals):
|
||||
adjust_coordinates(self, modals, 'text_x', 'text_y')
|
||||
adjust_repetition(self, modals)
|
||||
@ -1500,7 +1556,7 @@ class Text(Record):
|
||||
return size
|
||||
|
||||
|
||||
class Rectangle(Record):
|
||||
class Rectangle(Record, GeometryMixin):
|
||||
"""
|
||||
Rectangle record (ID 20)
|
||||
|
||||
@ -1558,6 +1614,14 @@ class Rectangle(Record):
|
||||
if is_square and self.height is not None:
|
||||
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):
|
||||
adjust_coordinates(self, modals, 'geometry_x', 'geometry_y')
|
||||
adjust_repetition(self, modals)
|
||||
@ -1635,7 +1699,7 @@ class Rectangle(Record):
|
||||
return size
|
||||
|
||||
|
||||
class Polygon(Record):
|
||||
class Polygon(Record, GeometryMixin):
|
||||
"""
|
||||
Polygon record (ID 21)
|
||||
|
||||
@ -1685,6 +1749,9 @@ class Polygon(Record):
|
||||
if len(point_list) < 3:
|
||||
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):
|
||||
adjust_coordinates(self, modals, 'geometry_x', 'geometry_y')
|
||||
adjust_repetition(self, modals)
|
||||
@ -1752,7 +1819,7 @@ class Polygon(Record):
|
||||
return size
|
||||
|
||||
|
||||
class Path(Record):
|
||||
class Path(Record, GeometryMixin):
|
||||
"""
|
||||
Polygon record (ID 22)
|
||||
|
||||
@ -1821,6 +1888,18 @@ class Path(Record):
|
||||
self.extension_start = extension_start
|
||||
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):
|
||||
adjust_coordinates(self, modals, 'geometry_x', 'geometry_y')
|
||||
adjust_repetition(self, modals)
|
||||
@ -1927,7 +2006,7 @@ class Path(Record):
|
||||
return size
|
||||
|
||||
|
||||
class Trapezoid(Record):
|
||||
class Trapezoid(Record, GeometryMixin):
|
||||
"""
|
||||
Trapezoid record (ID 23, 24, 25)
|
||||
|
||||
@ -2017,6 +2096,21 @@ class Trapezoid(Record):
|
||||
raise InvalidDataError('Trapezoid: w < 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):
|
||||
adjust_coordinates(self, modals, 'geometry_x', 'geometry_y')
|
||||
adjust_repetition(self, modals)
|
||||
@ -2103,7 +2197,7 @@ class Trapezoid(Record):
|
||||
|
||||
|
||||
# TODO: CTrapezoid type descriptions
|
||||
class CTrapezoid(Record):
|
||||
class CTrapezoid(Record, GeometryMixin):
|
||||
"""
|
||||
CTrapezoid record (ID 26)
|
||||
|
||||
@ -2161,6 +2255,23 @@ class CTrapezoid(Record):
|
||||
|
||||
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):
|
||||
adjust_coordinates(self, modals, 'geometry_x', 'geometry_y')
|
||||
adjust_repetition(self, modals)
|
||||
@ -2267,7 +2378,6 @@ class CTrapezoid(Record):
|
||||
size += self.repetition.write(stream) # type: ignore
|
||||
return size
|
||||
|
||||
|
||||
def check_valid(self):
|
||||
ctrapezoid_type = self.ctrapezoid_type
|
||||
width = self.width
|
||||
@ -2299,7 +2409,7 @@ class CTrapezoid(Record):
|
||||
'{}'.format(ctrapezoid_type))
|
||||
|
||||
|
||||
class Circle(Record):
|
||||
class Circle(Record, GeometryMixin):
|
||||
"""
|
||||
Circle record (ID 27)
|
||||
|
||||
@ -2344,6 +2454,9 @@ class Circle(Record):
|
||||
self.y = y
|
||||
self.repetition = repetition
|
||||
|
||||
def get_radius(self) -> int:
|
||||
return verify_modal(self.radius)
|
||||
|
||||
def merge_with_modals(self, modals: Modals):
|
||||
adjust_coordinates(self, modals, 'geometry_x', 'geometry_y')
|
||||
adjust_repetition(self, modals)
|
||||
|
Loading…
Reference in New Issue
Block a user