masque/masque/label.py

170 lines
4.2 KiB
Python
Raw Normal View History

2019-05-17 00:31:07 -07:00
from typing import List, Tuple, Dict
2018-08-30 23:07:14 -07:00
import copy
import numpy
from numpy import pi
2019-12-12 00:38:11 -08:00
from .error import PatternError, PatternLockedError
2018-09-02 20:01:25 -07:00
from .utils import is_scalar, vector2, rotation_matrix_2d
2018-08-30 23:07:14 -07:00
__author__ = 'Jan Petykiewicz'
class Label:
"""
2019-12-12 00:38:11 -08:00
A text annotation with a position and layer (but no size; it is not drawn)
2018-08-30 23:07:14 -07:00
"""
2019-12-12 00:38:11 -08:00
__slots__ = ('_offset', '_layer', '_string', 'identifier', 'locked')
2018-08-30 23:07:14 -07:00
# [x_offset, y_offset]
_offset: numpy.ndarray
2018-08-30 23:07:14 -07:00
# Layer (integer >= 0) or 2-Tuple of integers
_layer: int or Tuple
2018-08-30 23:07:14 -07:00
# Label string
_string: str
2018-08-30 23:07:14 -07:00
# Arbitrary identifier tuple
identifier: Tuple
2019-12-12 00:38:11 -08:00
locked: bool # If True, any changes to the label will raise a PatternLockedError
def __setattr__(self, name, value):
if self.locked and name != 'locked':
raise PatternLockedError()
object.__setattr__(self, name, value)
2018-08-30 23:07:14 -07:00
# ---- Properties
# offset property
@property
def offset(self) -> numpy.ndarray:
"""
[x, y] offset
:return: [x_offset, y_offset]
"""
return self._offset
@offset.setter
def offset(self, val: vector2):
if not isinstance(val, numpy.ndarray):
val = numpy.array(val, dtype=float)
if val.size != 2:
raise PatternError('Offset must be convertible to size-2 ndarray')
self._offset = val.flatten()
# layer property
@property
def layer(self) -> int or Tuple[int]:
"""
Layer number (int or tuple of ints)
:return: Layer
"""
return self._layer
@layer.setter
def layer(self, val: int or List[int]):
self._layer = val
# string property
@property
def string(self) -> str:
"""
Label string (str)
:return: string
"""
return self._string
@string.setter
def string(self, val: str):
self._string = val
def __init__(self,
string: str,
offset: vector2=(0.0, 0.0),
2019-12-12 00:38:11 -08:00
layer: int=0,
locked: bool = False):
self.unlock()
self.identifier = ()
2018-08-30 23:07:14 -07:00
self.string = string
self.offset = numpy.array(offset, dtype=float)
self.layer = layer
2019-12-12 00:38:11 -08:00
self.locked = locked
def __copy__(self) -> 'Label':
return Label(string=self.string,
offset=self.offset.copy(),
layer=self.layer,
locked=self.locked)
2018-08-30 23:07:14 -07:00
2019-05-17 00:31:07 -07:00
def __deepcopy__(self, memo: Dict = None) -> 'Label':
memo = {} if memo is None else memo
new = copy.copy(self)
new._offset = self._offset.copy()
return new
2018-08-30 23:07:14 -07:00
def copy(self) -> 'Label':
"""
Returns a deep copy of the shape.
:return: Deep copy of self
"""
return copy.deepcopy(self)
def translate(self, offset: vector2) -> 'Label':
"""
Translate the shape by the given offset
:param offset: [x_offset, y,offset]
:return: self
"""
self.offset += offset
return self
def rotate_around(self, pivot: vector2, rotation: float) -> 'Label':
"""
Rotate the shape around a point.
:param pivot: Point (x, y) to rotate around
:param rotation: Angle to rotate by (counterclockwise, radians)
:return: self
"""
pivot = numpy.array(pivot, dtype=float)
self.translate(-pivot)
self.offset = numpy.dot(rotation_matrix_2d(rotation), self.offset)
self.translate(+pivot)
return self
def get_bounds(self) -> numpy.ndarray:
"""
Return the bounds of the label.
Labels are assumed to take up 0 area, i.e.
bounds = [self.offset,
self.offset]
:return: Bounds [[xmin, xmax], [ymin, ymax]]
"""
return numpy.array([self.offset, self.offset])
2019-12-12 00:38:11 -08:00
def lock(self) -> 'Label':
"""
Lock the Label
2018-08-30 23:07:14 -07:00
2019-12-12 00:38:11 -08:00
:return: self
"""
object.__setattr__(self, 'locked', True)
return self
def unlock(self) -> 'Label':
"""
Unlock the Label
:return: self
"""
object.__setattr__(self, 'locked', False)
return self