Bounding-Boxes

Given the coordinates start=(xmin,ymin) and stop=(xmax,ymax) of the first and last 2-dimensional position in a rectangular region whose axes are aligned with the coordinate axes, a 2-D bounding-box representing this region is built by one of:

box = BoundingBox(start, stop)
box = BoundingBox(; start=..., stop=...)
box = BoundingBox((xmin,ymin), (xmax,ymax))
box = BoundingBox(; xmin=..., ymin=..., xmax=..., ymax=...)

All points of coordinates (x,y) such that xmin ≤ x ≤ xmax and ymin ≤ y ≤ ymax are considered as being part of the bounding-box. Hence, if xmin ≤ xmax and ymin ≤ ymax do not both hold, the box is empty as can be tested by isempty(box).

Arguments start and stop may also be two points (of type Point) or, for integer-valued coordinates, two Cartesian indices or two unit ranges:

box = BoundingBox(Point(xmin,ymin), Point(xmax,ymax))
box = BoundingBox(xmin:xmax, ymin:ymax)

As a convenience, all arguments can be given by a single tuple. For example:

box = BoundingBox((start, stop))
box = BoundingBox((xmin:xmax, ymin:ymax))

Coordinate type

The coordinate type, say T, can be explicitly specified with the constructor, for example:

box = BoundingBox{T}(start, stop)

If T is unspecified, it is inferred from the coordinate type of the arguments promoted to a common type.

The coordinate type of a bounding-box can be retrieved by the coord_type and eltype methods.

Converting an existing bounding-box to another coordinate type can done by convert_coord_type.

Aliases

Call:

using TwoDimensional: BoundingBox2D

instead of:

using TwoDimensional

to have BoundingBox2D provided as an alias to TwoDimensional.BoundingBox and possibly avoid conflict with other packages.

Construction

The coordinates of a bounding-box can be specified by keywords:

There are no default values for keywords xmin, xmax, ymin and ymax so all must be specified.

A bounding-box can be constructed from a 4-tuple of coordinates and conversely:

BoundingBox((x0,x1,y0,y1))      # yields BoundingBox(x0,x1,y0,y1)
Tuple(BoundingBox(x0,x1,y0,y1)) # yields (x0,x1,y0,y1)

A bounding-box can be constructed from its first and last points (i.e. at the lower-left and upper right opposite corners) specified as instances of Point, of CartesianIndex{2} or of Tuple{Real,Real}:

BoundingBox(Point(x0,y0), Point(x1,y1))
BoundingBox(CartesianIndex(x0,y0), CartesianIndex(x1,y1))
BoundingBox((x0,y0), (x1,y1))

which all yield the same result:

BoundingBox(x0,x1,y0,y1)

Conversely, methods first(B) and last(B) respectively yield the lower left and upper right corners of the bounding-box B (as a Point instance):

first(BoundingBox(x0,x1,y0,y1)) # yields Point(x0,y0)
last(BoundingBox(x0,x1,y0,y1))  # yields Point(x1,y1)

Integer-valued unit-ranges can be specified to define a bounding-box. For example:

BoundingBox(x0:x1, y0:y1)    # 2 unit-range
BoundingBox((x0:x1, y0:y1))  # a 2-tuple of unit range

This makes possible writing:

BoundingBox(axes(A))

to get the bounding-box corresponding to all indices of array A. Conversely:

axes(BoundingBox(x0,x1,y0,y1))

yields the axes of a bounding-box with integer coordinates, that is (x0:x1,y0:y1). To get the k-th axis of a bounding-box B, call axes(B,k).

To loop over the Cartesian indices defined by a bounding-box B with integer coordinates, you can just write:

for I in CartesianIndices(B)
   ...
end

A bounding-box may also be constructed by applying a predicate function to the elements of a 2-dimensional array:

BoundingBox(f, A)

yields the bounding-box of all integer coordinates (x,y) such that f(A[x,y]) yields true. If the elements of A are booleans (of type Bool), then BoundingBox(A) is equivalent to BoundingBox(identity,A).

Fields

The fields of a BoundingBox, say box, can be retrieved in different ways:

xmin = box.xmin
xmax = box.xmax
ymin = box.ymin
ymax = box.ymax

or:

xmin = box[1]
xmax = box[2]
ymin = box[3]
ymax = box[4]

or:

xmin, xmax, ymin, ymax = box

Conversion

Coordinate type conversion, say to type T, is done by:

B = BoundingBox(x0,x1,y0,y1)
BoundingBox{T}(B)
convert(BoundingBox{T}, B)
T.(B)

The latter form involves broadcasting rules and may be a bit slower.

Union and Intersection of Bounding-Boxes

The union of bounding-boxes b1, b2, ... is given by one of:

B1 ∪ B2 ∪ ...
union(B1, B2, ...)

which both yield the largest bounding-box contained into the bounding-boxes B1, B2, ...

The intersection of bounding-boxes B1, B2, ... is given by one of:

B1 ∩ B2 ∩ ...
intersect(B1, B2, ...)

which both yield the smallest bounding-box containing the bounding-boxes B1, B2, ...

The maximal or minimal bounding-box with coordinates of type T that can be constructed are respectively given by typemax(BoundingBox{T}) and typemin(BoundingBox{T}). These can be useful to initiate a shrinking or a growing bounding-box. The call:

BoundingBox{T}(nothing)

yields the same result as typemin(BoundingBox{T}).

Interior, Exterior, Nearest, etc.

Given the bounding-box B, interior(B) and exterior(B) respectively yield the largest interior and smallest exterior bounding-boxes with integer bounds.

round(B), round(T,B), or round(T,B,r) yield a bounding-box whose limits are those of the bounding-box B rounded to the nearest integral values of type T with rounding-mode r. Default type T is eltype(B) and default rounding-mode r is RoundingNearest.

center(B) yields the Point whose coordinates are the geometrical center of the bounding-box B.

area(B) yields the area of a bounding-box B.

Arithmetic and Basic Methods

Adding or subtracting a scalar δ to a bounding-box B adds or removes a margin δ to the bounding-box B:

BoundingBox((x0,y0),(x1,y1)) + δ -> BoundingBox((x0-δ,y0-δ),(x1+δ,y1+δ))
BoundingBox((x0,y0),(x1,y1)) - δ -> BoundingBox((x0+δ,y0+δ),(x1-δ,y1-δ))

Adding or subtracting a point P to a bounding-box B shifts the limits of the bounding-box B:

BoundingBox((x0,y0),(x1,y1)) + Point(x,y) -> BoundingBox((x0+x,y0+y),(x1+x,y1+y))

A bounding-box B can be negated:

-BoundingBox((x0,y0),(x1,y1)) -> BoundingBox((-x1,-y1),(-x0,-y0))

eltype(B) yields the type of the coordinates of a bounding-box B.

Basic methods size(B[,k]) and axes(B[,k]) can be applied to an integer-valued bounding-box B. These two methods are type-stable: size(B) yields a 2-tuple of Int, size(B,k) yields an Int, axes(B) yields a 2-tuple of UnitRange{Int} and axes(B,k) yields a UnitRange{Int}.

Method in and operator , obtained by \in-tab, yield whether a point pnt is inside a bounding-box box:

pnt ∈ box

is a shortcut for:

(box.xmin ≤ pnt.x ≤ box.xmax) & (box.ymin ≤ pnt.y ≤ box.ymax)

Method issubset or operator , obtained by \subseteq-tab, yield whether a bounding-box, say A, is inside another one, say B. That is issubset(A, B) and A ⊆ B are shortcuts for:

(isempty(A) | ((A.xmin ≥ B.xmin) & (A.xmax ≤ B.xmax) &
               (A.ymin ≥ B.ymin) & (A.ymax ≤ B.ymax)))