tuikit/core/coords.py
author Radek Brich <radek.brich@devl.cz>
Sun, 15 Feb 2015 12:52:46 +0100
changeset 115 b4ff7392003a
parent 108 11dac45bfba4
child 119 dd91747504dd
permissions -rw-r--r--
GridLayout: basic implementation.


class Point:

    """Point in cartesian space.

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

    This is heavy-weight mutable object. See also ImmutablePoint.

    """

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

    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)

    # sequence interface

    def __len__(self):
        return 2

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

    # point arithmetics

    def __add__(self, other):
        return Point(self.x + other[0], self.y + other[1])

    def __sub__(self, other):
        return Point(self.x - other[0], self.y - other[1])

    def __eq__(self, other):
        """Comparison operator.

        Point can be compared to any sequence of at least two elements:

        >>> p = Point(1, 2)
        >>> p == Point(1, 2)
        True
        >>> p == (1, 2)
        True
        >>> p == (0, 0)
        False
        >>> p == None
        False

        """
        try:
            return self.x == other[0] and self.y == other[1]
        except (TypeError, IndexError):
            return False

    # string representation

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

    def immutable(self):
        return ImmutablePoint(*self)


class ImmutablePoint:

    """Point class without write access."""

    __slots__ = ('_x', '_y')

    def __init__(self, x, y):
        self._x = x
        self._y = y

    @property
    def x(self):
        return self._x

    @property
    def y(self):
        return self._y

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

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

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


class Size:

    """Size class.

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

    This is heavy-weight mutable object. See also ImmutableSize.

    """

    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 '{0.__class__.__name__}(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 immutable(self):
        return ImmutableSize(*self)


class ImmutableSize:

    """Size class without write access.

    When using reference to (mutable) Size class, properties are not fixed.
    They can still be changed by original owner and these changes will become
    visible to other references of the object. ImmutableSize should be used
    when this is not intended.

    """

    __slots__ = ('_w', '_h')

    def __init__(self, w, h):
        self._w = w
        self._h = h

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

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

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

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


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 '{0.__class__.__name__}(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)