Box

Description

A Box is a rectangular area defined by two coordinates:

  • the top-left corner of the rectangle: the min coord,

  • the bottom-right corner of the rectangle: the max coord.

The default size of a Box is (1, 1), so you can create a box by only specifying the top-let corner of the rectangle.

>>> from benker.box import Box

>>> Box(1, 2, 2, 3)
Box(min=Coord(x=1, y=2), max=Coord(x=2, y=3))
>>> Box(1, 2)
Box(min=Coord(x=1, y=2), max=Coord(x=1, y=2))

You can use two coordinates to define a box:

>>> from benker.coord import Coord

>>> Box(Coord(5, 6), Coord(7, 8))
Box(min=Coord(x=5, y=6), max=Coord(x=7, y=8))
>>> Box(Coord(5, 6))
Box(min=Coord(x=5, y=6), max=Coord(x=5, y=6))

You can specify the size of a box:

>>> from benker.size import Size

>>> Box(Coord(5, 6), Size(3, 2))
Box(min=Coord(x=5, y=6), max=Coord(x=7, y=7))

We use the Excel convention to represent a Box: columns are represented by letters, rows are represented by numbers.

>>> print(Box(Coord(2, 5)))
B5
>>> print(Box(Coord(2, 5), Size(3, 2)))
B5:D6

Properties

You can use the following properties to extract information from a box:

  • use min to get the top-left corner coordinates,

  • use max to get the bottom-right corner coordinates,

  • use width to get the width of the box (number of columns),

  • use height to get the height of the box (number of rows),

  • use size to get the size (width and height) of the box.

>>> b1 = Box(Coord(5, 6), Size(3, 2))

>>> b1.min
Coord(x=5, y=6)
>>> b1.max
Coord(x=7, y=7)
>>> b1.width
3
>>> b1.height
2
>>> b1.size
Size(width=3, height=2)

Warning

All properties are non-mutable:

>>> b1.width = 9
Traceback (most recent call last):
...
AttributeError: can't set attribute

Operations

Contains

You can check if a point, defined by its coordinates (tuple (x, y) or Coord instance), is contained in a box:

>>> top_left = Coord(5, 6)
>>> top_right = Coord(6, 6)
>>> bottom_left = Coord(5, 8)
>>> bottom_right = Coord(6, 8)

>>> b1 = Box(top_left, bottom_right)

>>> top_left in b1
True
>>> top_right in b1
True
>>> bottom_left in b1
True
>>> bottom_right in b1
True

>>> Coord(7, 6) in b1
False

>>> (5, 7) in b1
True

Warning

Even if a Size object is a subtype of tuple, such an object cannot be “contained” in a Box.

>>> b1 = Box(Coord(x=5, y=6), Coord(x=6, y=8))
>>> Size(5, 7) in b1
Traceback (most recent call last):
    ...
TypeError: <class 'benker.size.Size'>

You can check if a Box is contained in another box:

>>> b1 = Box(Coord(x=5, y=6), Coord(x=6, y=8))
>>> b2 = Box(Coord(x=5, y=7), Coord(x=6, y=7))
>>> b3 = Box(Coord(x=6, y=6), Coord(x=7, y=6))

>>> b1 in b1
True
>>> b2 in b1
True
>>> b3 in b2
False

Intersection and Union

You can find if a Box intersects another Box:

>>> b1 = Box(Coord(x=1, y=1), Coord(x=3, y=3))
>>> b2 = Box(Coord(x=2, y=2), Coord(x=4, y=4))
>>> b3 = Box(Coord(x=4, y=1), Coord(x=5, y=1))

>>> b1.intersect(b2)
True
>>> b1.intersect(b3)
False

Two boxes are disjoint if they don’t intersect each other:

>>> b1.isdisjoint(b2)
False
>>> b1.isdisjoint(b3)
True

You can calculate the intersection of two boxes. You can use the “&” operator to do that:

>>> b1.intersection(b2)
Box(min=Coord(x=2, y=2), max=Coord(x=3, y=3))
>>> b1 & b2
Box(min=Coord(x=2, y=2), max=Coord(x=3, y=3))

Warning

If the two boxes are disjoint, there is no intersection:

>>> b1 & b3
Traceback (most recent call last):
  ...
ValueError: (Box(min=Coord(x=1, y=1), max=Coord(x=3, y=3)), Box(min=Coord(x=4, y=1), max=Coord(x=5, y=1)))

You can calculate the union of two boxes. The union of two boxes is the bounding box: You can use the “|” operator to do that:

>>> b1.union(b2)
Box(min=Coord(x=1, y=1), max=Coord(x=4, y=4))
>>> b1 | b2
Box(min=Coord(x=1, y=1), max=Coord(x=4, y=4))

Total ordering

A total ordering is defined for the boxes. The aim is to order the cells in a grid sorted from left to right and from top to bottom. This order is useful to group the cells by rows.

You can compare boxes:

>>> b1 = Box(Coord(3, 2), Coord(6, 4))
>>> b1 < b1
False
>>> b1 < Box(Coord(3, 2), Coord(6, 5))
True
>>> b1 < Box(Coord(3, 2), Coord(7, 4))
True
>>> b1 < Box(Coord(4, 2), Coord(6, 4))
True
>>> b1 < Box(Coord(3, 3), Coord(6, 4))
True

You can sort boxes. The sort order can be defined as below:

  • top cells are sorted before bottom cells,

  • top-left cells are sorted before top-right cells,

  • smaller cells are sorted before bigger.

>>> from random import shuffle

>>> boxes = [Box(x, y) for x in range(1, 4) for y in range(1, 3)]
>>> [str(box) for box in boxes]
['A1', 'A2', 'B1', 'B2', 'C1', 'C2']

>>> shuffle(boxes)
>>> [str(box) for box in sorted(boxes)]
['A1', 'B1', 'C1', 'A2', 'B2', 'C2']