masque/masque/label.py

117 lines
3.4 KiB
Python
Raw Normal View History

from typing import Tuple, Dict, Optional, TypeVar
2018-08-30 23:07:14 -07:00
import copy
import numpy # type: ignore
2018-08-30 23:07:14 -07:00
2020-07-22 21:50:39 -07:00
from .repetition import Repetition
2020-09-18 19:47:31 -07:00
from .utils import vector2, rotation_matrix_2d, layer_t, AutoSlots, annotations_t
2020-07-22 21:50:39 -07:00
from .traits import PositionableImpl, LayerableImpl, Copyable, Pivotable, LockableImpl, RepeatableImpl
from .traits import AnnotatableImpl
2018-08-30 23:07:14 -07:00
L = TypeVar('L', bound='Label')
class Label(PositionableImpl, LayerableImpl, LockableImpl, RepeatableImpl, AnnotatableImpl,
2020-07-22 21:50:39 -07:00
Pivotable, Copyable, metaclass=AutoSlots):
2018-08-30 23:07:14 -07:00
"""
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
"""
__slots__ = ( '_string', 'identifier')
2018-08-30 23:07:14 -07:00
_string: str
""" Label string """
2018-08-30 23:07:14 -07:00
identifier: Tuple
""" Arbitrary identifier tuple, useful for keeping track of history when flattening """
'''
---- Properties
'''
2018-08-30 23:07:14 -07:00
# string property
@property
def string(self) -> str:
"""
Label string (str)
"""
return self._string
@string.setter
def string(self, val: str):
self._string = val
def __init__(self,
string: str,
*,
2020-05-11 19:10:00 -07:00
offset: vector2 = (0.0, 0.0),
2020-05-11 18:39:02 -07:00
layer: layer_t = 0,
2020-07-22 21:50:39 -07:00
repetition: Optional[Repetition] = None,
annotations: Optional[annotations_t] = None,
locked: bool = False,
) -> None:
LockableImpl.unlock(self)
self.identifier = ()
2018-08-30 23:07:14 -07:00
self.string = string
2020-05-11 18:49:30 -07:00
self.offset = numpy.array(offset, dtype=float, copy=True)
2018-08-30 23:07:14 -07:00
self.layer = layer
2020-07-22 21:50:39 -07:00
self.repetition = repetition
self.annotations = annotations if annotations is not None else {}
self.set_locked(locked)
2019-12-12 00:38:11 -08:00
def __copy__(self: L) -> L:
2020-11-09 22:04:04 -08:00
return type(self)(string=self.string,
offset=self.offset.copy(),
layer=self.layer,
repetition=self.repetition,
locked=self.locked)
2018-08-30 23:07:14 -07:00
def __deepcopy__(self: L, memo: Dict = None) -> L:
2019-05-17 00:31:07 -07:00
memo = {} if memo is None else memo
2019-12-13 01:25:38 -08:00
new = copy.copy(self).unlock()
2019-05-17 00:31:07 -07:00
new._offset = self._offset.copy()
new.set_locked(self.locked)
2019-05-17 00:31:07 -07:00
return new
2018-08-30 23:07:14 -07:00
def rotate_around(self: L, pivot: vector2, rotation: float) -> L:
2018-08-30 23:07:14 -07:00
"""
Rotate the label around a point.
Args:
pivot: Point (x, y) to rotate around
rotation: Angle to rotate by (counterclockwise, radians)
2018-08-30 23:07:14 -07:00
Returns:
self
2018-08-30 23:07:14 -07:00
"""
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]
Returns:
Bounds [[xmin, xmax], [ymin, ymax]]
2018-08-30 23:07:14 -07:00
"""
return numpy.array([self.offset, self.offset])
def lock(self: L) -> L:
PositionableImpl._lock(self)
LockableImpl.lock(self)
2019-12-12 00:38:11 -08:00
return self
def unlock(self: L) -> L:
LockableImpl.unlock(self)
PositionableImpl._unlock(self)
2019-12-12 00:38:11 -08:00
return self
def __repr__(self) -> str:
locked = ' L' if self.locked else ''
return f'<Label "{self.string}" l{self.layer} o{self.offset}{locked}>'