# 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']
```