tuikit/core/coords.py
author Radek Brich <radek.brich@devl.cz>
Mon, 01 Sep 2014 08:45:51 +0200
changeset 104 742e504ec053
parent 97 0c2e0c09ba5c
child 106 abcadb7e2ef1
permissions -rw-r--r--
Update TextBox: Replace "spot" with "cursor".

class Point:

    """Point in cartesian space.

    Implements attribute access (.x, .y) and list-like access([0],[1]).

    """

    def __init__(self, *args, **kwargs):
        self.x = 0
        self.y = 0
        self.update(*args, **kwargs)

    def __getitem__(self, key):
        return (self.x, self.y)[key]

    def __repr__(self):
        return 'Point(x={0.x},y={0.y})'.format(self)

    def move(self, relx, rely):
        self.x += relx
        self.y += rely

    def moved(self, relx, rely):
        return Point(self.x + relx, self.y + rely)

    def update(self, *args, **kwargs):
        """Update point.

        Accepts positional or keyword arguments:
        - (point: Point)
        - (x: int, y: int)

        """
        if len(args) == 2:
            self.x, self.y = args
        elif len(args) == 1:
            self.x, self.y = args[0]
        elif len(args):
            raise ValueError('Too many args.')
        for key, val in kwargs.items():
            if key == 'point':
                self.x, self.y = val
            elif key in ('x', 'y'):
                setattr(self, key, val)
            else:
                raise ValueError('Bad keyword arg: %r' % key)


class Size:

    """Size class.

    Implements attribute access (.w, .h) and list-like access([0],[1]).

    """

    def __init__(self, *args, **kwargs):
        self.w = 0
        self.h = 0
        self.update(*args, **kwargs)

    def __getitem__(self, key):
        return (self.w, self.h)[key]

    def __repr__(self):
        return 'Size(w={0.w},h={0.h})'.format(self)

    def update(self, *args, **kwargs):
        """Update size.

        Accepts positional or keyword arguments:
        - (size: Size)
        - (w: int, h: int)

        """
        if len(args) == 2:
            self.w, self.h = args
        elif len(args) == 1:
            self.w, self.h = args[0]
        elif len(args):
            raise ValueError('Too many args.')
        for key, val in kwargs.items():
            if key == 'size':
                self.w, self.h = val
            elif key in ('w', 'h'):
                setattr(self, key, val)
            else:
                raise ValueError('Bad keyword arg: %r' % key)

    def readonly(self):
        return ReadonlySize(self)


class ReadonlySize:

    """Wrapper for Size which makes it read-only."""

    def __init__(self, size):
        self._size = size

    @property
    def w(self):
        return self._size.w

    @property
    def h(self):
        return self._size.h

    def __getitem__(self, key):
        return self._size[key]

    def __repr__(self):
        return 'ReadonlySize(w={0.w},h={0.h})'.format(self._size)


class Rect:

    """Rectangle is defined by coordinates and size."""

    def __init__(self, x=0, y=0, w=0, h=0):
        self.x = x
        self.y = y
        self.w = w
        self.h = h

    @classmethod
    def _make(cls, origin, size):
        """Make new Rect instance with origin and size as specified.

        `origin` should be Point or pair of coordinates,
        `size` should be Size or pair of integers

        """
        x, y = origin
        w, h = size
        return Rect(x, y, w, h)

    def __repr__(self):
        return 'Rect(x={0.x},y={0.y},w={0.w},h={0.h})'.format(self)

    def __contains__(self, point):
        """Test if point is positioned inside rectangle.

        `point` should be either Point or pair of int values.

        """
        x, y = point
        return (self.x <= x < self.x + self.w
            and self.y <= y < self.y + self.h)

    def moved(self, relx, rely):
        """Return new Rect with same size, moved by `relx`, `rely`."""
        return Rect(self.x + relx, self.y + rely, self.w, self.h)

    def intersect(self, other):
        x1 = max(self.x, other.x)
        y1 = max(self.y, other.y)
        x2 = min(self.x + self.w, other.x + other.w)
        y2 = min(self.y + self.h, other.y + other.h)
        if x1 >= x2 or y1 >= y2:
            return Rect()
        return Rect(x1, y1, x2-x1, y2-y1)

    def union(self, other):
        x = min(self.x, other.x)
        y = min(self.y, other.y)
        w = max(self.x + self.w, other.x + other.w) - x
        h = max(self.y + self.h, other.y + other.h) - y
        return Rect(x, y, w, h)