Reference

Range

Base.containsMethod
contains(x::UnitRange{Int}, y::Integer)

Test if range x contains value y.

In addition to the contains method, you can also use the ∈ operator.

julia> using Regions

julia> contains(0:10, 5)
true

julia> contains(0:10, 15)
false

julia> 0 ∈ 0:10
true

julia> 100 ∈ 0:10
false
source
Regions.invertMethod
invert(x::UnitRange{Int})

Invert a range. Inversion mirrors a range at the origin. A range is inverted by reversing and inverting each of its coordinates.

In addition to the invert method, you can also use the - operator to invert a range.

julia> using Regions

julia> invert(5:10)
-10:-5

julia> -(5:10)
-10:-5

julia> invert(invert(0:100))
0:100

julia> invert(-(0:100))
0:100
source
Regions.iscloseMethod
isclose(x::UnitRange{Int}, y::UnitRange{Int}, distance::Integer)

Test whether the gap between x and y is at most distance integers wide. Equivalently, isclose(x, y, d) returns true iff growing the smaller range by d units toward the other range makes them overlap.

The distance parameter generalizes the related range predicates:

  • distance == 0 is equivalent to isoverlapping — the ranges share at least one integer.
  • distance == 1 is equivalent to istouching — the ranges overlap or are immediately adjacent.
  • distance > 1 permits a gap of up to distance - 1 integers between them.
julia> using Regions

julia> isclose(0:10, 15:25, 5)
true

julia> isclose(0:10, 15:25, 4)
false
source
Regions.isoverlappingMethod
isoverlapping(x::UnitRange{Int}, y::UnitRange{Int})

Test if two ranges overlap.

julia> using Regions

julia> isoverlapping(0:10, 5:15)
true

julia> isoverlapping(0:10, 20:30)
false
source
Regions.istouchingMethod
istouching(x::UnitRange{Int}, y::UnitRange{Int})

Test if two ranges touch.

julia> using Regions

julia> istouching(0:10, 11:21)
true

julia> istouching(0:10, 12:22)
false
source
Regions.translateMethod
translate(x::UnitRange{Int}, y::Integer)

Translate a range. Translation moves a range. A range is translated by adding an offset to each of its coordinates.

In addition to the translate method, you can also use the + or - operators to translate a range.

julia> using Regions

julia> translate(0:10, 5)
5:15

julia> (5:15) + 10
15:25

julia> 10 + (5:15)
15:25

julia> (5:15) - 10
-5:5
source

Run

Regions.RunType
Run

A run is a set of consecutive coordinates within a column (possibly partial) of a region. It consists of a discrete column coordinate (of type Int) and a range of discrete row coordinates (of type UnitRange{Int}).

Runs within a region specify a sort order: one run is smaller than the other if it starts before the other run modeling the coordinates from left to right and top to bottom.

source
Base.containsMethod
contains(r::Run, x::Integer, y::Integer)
contains(r::Run, a::Vector{Int})

Test if run r contains position (x, y).

julia> using Regions

julia> contains(Run(7, 2:8), 7, 5)
true

julia> contains(Run(7, 2:8), [7, 5])
true

julia> contains(Run(7, 2:8), 7, 10)
false

julia> contains(Run(7, 2:8), 3, 5)
false
source
Base.isemptyMethod
isempty(x::Run)

Discover whether the run is empty.

julia> using Regions

julia> isempty(Run(1, 1:10))
false

julia> isempty(Run(2, 1:1))
false

julia> isempty(Run(3, 1:0))
true
source
Base.islessMethod
isless(x::Run, y::Run)

Compare two runs according to their natural sort order. First, their columns are compared, and if they are equal, their row ranges are compared.

julia> using Regions

julia> isless(Run(0, 1:10), Run(1, 0:10))
true

julia> isless(Run(1, 1:10), Run(1, 2:10))
true
source
Regions._minkowski_additionMethod
_minkowski_addition(a::Run, b::Run)

Compute the Minkowski sum of two runs.

Each run represents a vertical segment at a given column: Run(col, rows). The Minkowski sum is the segment whose column is a.column + b.column and whose row range is the interval sum (a.rows.start + b.rows.start):(a.rows.stop + b.rows.stop).

This is the primitive used by _minkowski_addition(::Region, ::Region).

source
Regions._minkowski_subtractionMethod
_minkowski_subtraction(a::Run, b::Run)

Compute the Minkowski difference of two runs.

Each run represents a vertical segment at a given column: Run(col, rows). The Minkowski difference a ⊖ b is the segment whose column is a.column - b.column and whose row range is (a.rows.start - b.rows.start):(a.rows.stop - b.rows.stop). The result may be empty when b's row range is wider than a's.

This is the primitive used by _minkowski_subtraction(::Region, ::Region).

source
Regions.invertMethod
invert(x::Run)
-(x::Run) = invert(x)

Invert a run. Inversion mirrors a run at the origin. A run is inverted by negating its column and inverting its rows.

In addition to the invert method, you can also use the unary - operator.

julia> using Regions

julia> invert(Run(1, 20:30))
Run(-1, -30:-20)

julia> -Run(-1, -30:-20)
Run(1, 20:30)
source
Regions.iscloseMethod
isclose(a::Run, b::Run, x::Integer, y::Integer)
isclose(a::Run, b::Run, d::Integer)
isclose(x::Run, y::Run, distance::Vector{Int})

Test if two runs are close.

If distance == 0 this is the same as isoverlapping(). If distance == 1 this is the same as istouching(). If distance > 1 this is testing of closeness.

julia> using Regions

julia> isclose(Run(5, 0:10), Run(8, 2:15), 5, 3)
true

julia> isclose(Run(5, 0:10), Run(8, 15:20), 2, 2)
false
source
Regions.isoverlappingMethod
isoverlapping(x::Run, y::Run)

Test if two runs overlap. Two runs can only overlap if they share the same column.

julia> using Regions

julia> isoverlapping(Run(3, 0:10), Run(3, 5:15))
true

julia> isoverlapping(Run(3, 0:10), Run(3, 20:30))
false

julia> isoverlapping(Run(3, 0:10), Run(4, 0:10))
false
source
Regions.istouchingMethod
istouching(x::Run, y::Run)

Test if two runs touch.

julia> using Regions

julia> istouching(Run(5, 0:10), Run(6, 5:15))
true

julia> istouching(Run(5, 0:10), Run(7, 5:15))
false
source
Regions.translateMethod
translate(r::Run, x::Integer, y::Integer)
translate(r::Run, a::Vector{Int})

Translate a run. Translation moves a run. A run is translated by adding offsets to its column and rows.

In addition to the translate method, you can also use the + or - operators to translate a run.

julia> using Regions

julia> translate(Run(1, 20:30), 10, 20)
Run(11, 40:50)

julia> translate(Run(3, 2:5), [4, 10])
Run(7, 12:15)

julia> Run(3, 2:5) + [4, 10]
Run(7, 12:15)

julia> [4, 3] + Run(0, 0:10)
Run(4, 3:13)

julia> Run(0, 0:100) - [5, 25]
Run(-5, -25:75)
source

Region

Regions.RegionType
Region

A region is a discrete set of coordinates in two-dimensional euclidean space, represented as a sorted vector of Runs plus a boolean complement flag.

Sort invariant

The runs are sorted first by column, then by rows.start. Many functions in the package rely on this invariant — out-of-order runs will silently produce wrong results. If you mutate r.runs directly, restore the order with sort!(r.runs) before passing the region to other functions.

The complement flag

When complement == false (the usual case), the runs enumerate the pixels contained in the region. When complement == true, the runs enumerate the pixels excluded from an otherwise infinite plane — i.e. the region is the set-theoretic complement of those runs. This flag is what allows the package to represent infinite regions (such as the result of inverting a finite region) without storing infinite memory, and it is what enables union, intersection, and difference to handle complement operands correctly via De Morgan's laws. Use complement to construct a complement region rather than setting the field directly.

Examples

julia> using Regions

julia> Region() # create an empty region
Region(Run[], false)

julia> Region([Run(-1, -1:1), Run(0, -1:1), Run(1, -1:1)]) # create a region with 3 runs
Region(Run[Run(-1, -1:1), Run(0, -1:1), Run(1, -1:1)], false)

julia> Region([Run(col, -10:10) for col=-10:10]); # create a region with many runs using comprehension
source
Base.:(==)Method
==(a::Region, b::Region)

Equality operator for two regions. Two regions are equal, if both their runs and their complement flags are equal.

julia> using Regions

julia> a = Region([Run(-1, -1:1), Run(0, -1:1), Run(1, -1:1)]);

julia> b = Region([Run(-1, -1:1), Run(0, -1:1), Run(1, -1:1)]);

julia> a == b
true
source
Base.containsMethod
contains(r::Region, x::Integer, y::Integer)
contains(r::Region, a::Vector{Int})

Test if region r contains position (x, y). x is the column coordinate, y is the row coordinate.

julia> using Regions

julia> r = Region([Run(2, 1:4)]);

julia> contains(r, 2, 3)
true

julia> contains(r, 2, 5)
false

julia> contains(r, 3, 3)
false

julia> [2, 3] ∈ r
true
source
Base.copyMethod
copy(x::Region)

Create a copy of a region.

julia> using Regions

julia> a = Region([Run(-1, -1:1), Run(0, -1:1), Run(1, -1:1)]);

julia> b = copy(a)
Region(Run[Run(-1, -1:1), Run(0, -1:1), Run(1, -1:1)], false)
source
Base.isemptyMethod
isempty(x::Region)

Discover whether the region is empty.

julia> using Regions

julia> isempty(Region(Run[]))
true

julia> isempty(Region([Run(2, 1:1)]))
false
source
Base.showMethod
Base.show(io, mime::MIME"image/png", r::Region)

MIME"image/png" show method for a Region. Renders the region as a PNG via region_to_image with a half-transparent blue color (RGBA(0, 0, 1, 0.5)). This is what produces an inline graphic when a region is the value of the last expression in notebook environments such as Pluto, Jupyter, or VS Code, or when a region is passed to display. Empty regions render to nothing.

source
Base.unionMethod
union(a::Region, b::Region)

Returns the set-theoretic union of a and b — every pixel contained in a, in b, or in both. The operation is commutative and associative.

Complement regions are handled transparently: any combination of regular and complement operands is mapped via De Morgan's laws to operations on regular runs, and the result is returned with the appropriate complement flag.

julia> using Regions

julia> a = region_from_box(0, 0, 4, 3);

julia> b = region_from_box(3, 2, 7, 5);

julia> area(union(a, b))
36
source
Regions._differenceMethod
_difference(a::Vector{Run}, b::Vector{Run})

Calculate the difference of two sorted vectors of runs (elements in a not in b).

source
Regions._intersect!Method
_intersect!(a::Vector{Run})

Reduce a sorted array of runs to only the pairwise-intersecting portions.

source
Regions._intersectionMethod
_intersection(a::Vector{Run}, b::Vector{Run})

Calculate the intersection of two sorted arrays of runs.

source
Regions._mergeMethod
_merge(a::Vector{Run}, b::Vector{Run})

Merge sorted vectors a and b. Assumes that a and b are sorted and does not check whether a or b are sorted.

source
Regions._pack!Method
_pack!(a::Vector{Run})

Pack consecutive runs in the same column that touch or overlap into a single run.

source
Regions._unionMethod
_union(a::Vector{Run}, b::Vector{Run})

Calculate the union of two sorted arrays of runs.

source
Regions.bottomMethod
bottom(x::Region)

Returns the largest row coordinate of x. Under the image-coordinate convention used by the package this is the bottommost row of the region (the row with the largest index).

Only valid for non-complement, non-empty regions.

julia> using Regions

julia> bottom(region_from_box(2, 1, 7, 5))
5
source
Regions.boundsMethod
bounds(x::Region)

Returns the bounding box of x as (left, top, right, bottom) — equivalently (min column, min row, max column, max row). Under the image-coordinate convention used by the package, top < bottom (rows increase downward).

Only valid for non-complement, non-empty regions.

julia> using Regions

julia> bounds(region_from_box(2, 1, 7, 5))
(2, 1, 7, 5)
source
Regions.center_regionMethod
center_region(r::Region) -> Region

Translate a non-empty, non-complement region so that its bounding-box centre lands as close to the origin as possible.

The translation amounts are computed from the integer midpoints of the column and row extents:

Δcol = (left(r) + right(r))  ÷ 2
Δrow = (bottom(r) + top(r))  ÷ 2

Because pixel coordinates are integers, even-width or even-height regions cannot be placed symmetrically; the standard integer (floor) division places the origin one pixel left of / below the geometric centre in those cases.

julia> using Regions

julia> r = Region([Run(3, 2:4), Run(4, 2:4), Run(5, 2:4)]);

julia> c = center_region(r);

julia> left(c), right(c), bottom(c), top(c)
(-1, 1, 1, -1)
source
Regions.complementMethod
complement(x::Region)

Calculates the set-theoretic complement of a region.

With a non-complemented region, the runs specify the contained pixels, i.e. they specify what is included within the region. With a complemented region, the runs specify the non-contained pixels, i.e. they specify what is not included within the region.

julia> using Regions

julia> r = Region([Run(0, 0:5)]);

julia> c = complement(r);

julia> c.complement
true

julia> contains(r, 0, 3)
true

julia> contains(c, 0, 3)
false

julia> contains(c, 0, 9)
true
source
Regions.differenceMethod
difference(a::Region, b::Region)

Returns the set-theoretic difference a \ b — every pixel contained in a but not in b. The operation is asymmetric: difference(a, b) and difference(b, a) are generally different regions.

Complement regions are handled transparently via De Morgan's laws.

julia> using Regions

julia> a = region_from_box(0, 0, 4, 3);

julia> b = region_from_box(3, 2, 7, 5);

julia> area(difference(a, b))
16

julia> area(difference(b, a))
16
source
Regions.intersectionMethod
intersection(a::Region, b::Region)

Returns the set-theoretic intersection of a and b — only the pixels contained in both. Useful for masking: intersect a segmented region with a geometric region of interest (a box, circle, polygon, …) to restrict subsequent analysis to that area.

Complement regions are handled transparently via De Morgan's laws.

julia> using Regions

julia> a = region_from_box(0, 0, 4, 3);

julia> b = region_from_box(3, 2, 7, 5);

julia> area(intersection(a, b))
4
source
Regions.invertMethod
invert(x::Region)
-(x::Region)

Invert a region. Inversion mirrors a region at the origin. A region is inverted by inverting each of its runs. Since the runs of a region are sorted by their column and row coordinates, the order of the runs is inversed as well.

In addition to the invert method, you can also use the unary - operator.

julia> using Regions

julia> a = Region([Run(0, 0:2), Run(1, 0:2), Run(2, 0:2)]);

julia> b = invert(a)
Region(Run[Run(-2, -2:0), Run(-1, -2:0), Run(0, -2:0)], false)

julia> c = -(a)
Region(Run[Run(-2, -2:0), Run(-1, -2:0), Run(0, -2:0)], false)

julia> a == invert(-a)
true
source
Regions.is_centeredMethod
is_centered(r::Region) -> Bool

Return true if the bounding-box midpoint of r is already at the origin, i.e. if center_region would leave r unchanged.

Uses the same integer midpoint arithmetic as center_region: Δcol = (left + right) ÷ 2, Δrow = (bottom + top) ÷ 2.

julia> using Regions

julia> is_centered(Region([Run(-1, -1:1), Run(0, -1:1), Run(1, -1:1)]))
true

julia> is_centered(Region([Run(3, 2:4), Run(4, 2:4), Run(5, 2:4)]))
false

julia> is_centered(center_region(Region([Run(3, 2:4), Run(4, 2:4), Run(5, 2:4)])))
true
source
Regions.leftMethod
left(x::Region)

Returns the smallest column coordinate of x — its leftmost column.

Only valid for non-complement, non-empty regions.

julia> using Regions

julia> left(region_from_box(2, 1, 7, 5))
2
source
Regions.region_from_boxMethod
region_from_box(left::Integer, top::Integer, right::Integer, bottom::Integer)

Create a filled rectangular region from its bounding-box coordinates. The argument order is left, top, right, bottom, and the package's image-coordinate convention requires top < bottom and left < right (rows increase downward, columns increase to the right). The result contains one vertical run per column, each spanning rows top:bottom.

julia> using Regions

julia> r = region_from_box(1, 1, 3, 4);

julia> bounds(r)
(1, 1, 3, 4)

julia> area(r)
12
source
Regions.region_from_circleMethod
region_from_circle(cx::Integer, cy::Integer, radius::Integer)

Create a filled circular region with center (cx, cy) and the given radius.

All integer coordinates (x, y) satisfying (x − cx)² + (y − cy)² ≤ radius² are included. Integer square root is used to avoid floating-point rounding issues.

julia> using Regions

julia> region_from_circle(0, 0, 0)
Region(Run[Run(0, 0:0)], false)

julia> region_from_circle(0, 0, 2)
Region(Run[Run(-2, 0:0), Run(-1, -1:1), Run(0, -2:2), Run(1, -1:1), Run(2, 0:0)], false)

julia> contains(region_from_circle(0, 0, 5), 0, 5)
true

julia> contains(region_from_circle(0, 0, 5), 4, 4)
false
source
Regions.region_from_ellipseMethod
region_from_ellipse(cx, cy, rx, ry, phi)
region_from_ellipse(e::NamedTuple)

Create a filled region from an ellipse with center (cx, cy), semi-axes rx (along phi) and ry (perpendicular), and rotation angle phi in radians.

The second form accepts a named tuple (center, semi_axes, angle) as returned by equivalent_ellipse, enabling round-trip conversion.

julia> using Regions

julia> r = region_from_ellipse(0.0, 0.0, 2.0, 1.0, 0.0);

julia> area(r)
4

julia> contains(r, 0, 0)
true
source
Regions.region_from_line_segmentMethod
region_from_line_segment(x0::Real, y0::Real, x1::Real, y1::Real)

Create a region containing all pixels on the 8-connected Bresenham line from (x0, y0) to (x1, y1). Endpoints are rounded to the nearest integer.

julia> using Regions

julia> area(region_from_line_segment(0.0, 0.0, 3.0, 0.0))
4

julia> area(region_from_line_segment(0.0, 0.0, 0.0, 3.0))
4

julia> area(region_from_line_segment(2.0, 2.0, 2.0, 2.0))
1
source
Regions.region_from_pointMethod
region_from_point(x::Real, y::Real)

Create a single-pixel region at the nearest integer coordinate to (x, y). Rounding uses trunc(Int, v + 0.5), matching C++ (int)(v + 0.5).

julia> using Regions

julia> r = region_from_point(1.4, 2.6);

julia> area(r)
1

julia> contains(r, 1, 3)
true
source
Regions.region_from_point_listMethod
region_from_point_list(points::Vector{Tuple{Int,Int}})

Create a region from a list of integer pixel coordinates (column, row). Each point becomes one pixel; duplicate points are merged.

julia> using Regions

julia> r = region_from_point_list([(0,0),(1,0),(1,1)]);

julia> area(r)
3

julia> contains(r, 1, 1)
true
source
Regions.region_from_polygonMethod
region_from_polygon(vertices::Vector{Tuple{Int,Int}})

Create a filled region from a polygon defined by its vertices as (column, row) pairs, given in either clockwise or counter-clockwise order.

The fill uses the half-open interval convention: for each edge the column with the smaller x-coordinate is included and the column with the larger x-coordinate is excluded. As a result, the rightmost column of the polygon is not filled. Horizontal edges are ignored.

julia> using Regions

julia> r = region_from_polygon([(0,0), (4,0), (2,4)]);

julia> length(r.runs)
4

julia> r.runs
4-element Vector{Run}:
 Run(0, 0:0)
 Run(1, 0:2)
 Run(2, 0:4)
 Run(3, 0:2)

julia> contains(r, 2, 2)
true

julia> contains(r, 0, 2)
false
source
Regions.region_from_ringMethod
region_from_ring(cx::Integer, cy::Integer, outer_radius::Integer, inner_radius::Integer)

Create a ring-shaped region: the set difference of two concentric circles with the same center (cx, cy). outer_radius must be ≥ inner_radius ≥ 0.

julia> using Regions

julia> r = region_from_ring(0, 0, 5, 3);

julia> area(r) == area(region_from_circle(0, 0, 5)) - area(region_from_circle(0, 0, 3))
true
source
Regions.region_to_imageFunction
region_to_image(r::Region, color=Gray(true))

Render a region to a 2D Array whose element type matches color. The image is sized to the region's bounding box: (bottom - top + 1, right - left + 1) rows by columns. Pixels outside the region are zero (e.g. Gray(0), RGB(0,0,0), or RGBA(0,0,0,0)); pixels inside are set to color. The element type of the returned array is typeof(color), so calling with Gray, RGB, or RGBA selects the output format.

Some examples of colors you can pass:

  • Gray(0.5) — mid gray
  • RGB(1, 0, 0) — bright red
  • RGBA(0, 0.5, 0, 0.5) — half-transparent mid green

Use this when you need a raster image (for saving, displaying, or interop with image libraries). For inline display in REPL/notebook contexts the Base.show method registered for MIME"image/png" calls this function automatically with a half-transparent blue color.

source
Regions.rightMethod
right(x::Region)

Returns the largest column coordinate of x — its rightmost column.

Only valid for non-complement, non-empty regions.

julia> using Regions

julia> right(region_from_box(2, 1, 7, 5))
7
source
Regions.topMethod
top(x::Region)

Returns the smallest row coordinate of x. Under the image-coordinate convention used by the package this is the topmost row of the region (the row with the smallest index).

Only valid for non-complement, non-empty regions.

julia> using Regions

julia> top(region_from_box(2, 1, 7, 5))
1
source
Regions.translateMethod
translate(r::Region, x::Integer, y::Integer)
translate(r::Region, a::Vector{Int})

Translate a region. Translation moves a region. A region is translated by translating each of its runs.

In addition to the translate method, you can also use the + or - operators to translate a region.

julia> using Regions

julia> a = Region([Run(0, 0:2), Run(1, 0:2), Run(2, 0:2)]);

julia> b = translate(a, -1, -1)
Region(Run[Run(-1, -1:1), Run(0, -1:1), Run(1, -1:1)], false)
source
Base.showMethod
Base.show(io, mime::MIME"image/png", regions::Vector{Region})

MIME"image/png" show method for a Vector{Region}. Renders the components as a PNG via regions_to_image, cycling through six half-transparent colors (blue, green, red, cyan, magenta, yellow) so that adjacent components are visually distinct. This is what produces inline graphics for the result of components in notebook environments.

source
Regions.bottomMethod
bottom(x::Vector{Region})

Calculates the maximum row coordinate across a vector of regions — the bottommost row under the package's image-coordinate convention. Returns missing for an empty vector.

julia> using Regions

julia> bottom([Region([Run(2, 1:3)]), Region([Run(7, 5:8)])])
8

julia> ismissing(bottom(Region[]))
true
source
Regions.boundsMethod
bounds(x::Vector{Region})

Calculates (left, top, right, bottom) — the minimum column, minimum row, maximum column, and maximum row — across all regions in the vector. Under the image-coordinate convention, top < bottom. Returns missing for an empty vector.

julia> using Regions

julia> bounds([Region([Run(2, 1:3)]), Region([Run(7, 2:6)])])
(2, 1, 7, 6)

julia> ismissing(bounds(Region[]))
true
source
Regions.closingMethod
closing(regions::Vector{Region}, se::Region)

Apply closing to each region in regions with structuring element se, returning a new vector that contains only the non-empty results.

julia> using Regions

julia> gapped = Region([Run(-1, 0:0), Run(1, 0:0)]);

julia> se = region_from_box(-1, -1, 1, 1);

julia> c = closing([gapped], se);

julia> contains(c[1], 0, 0)
true
source
Regions.dilationMethod
dilation(regions::Vector{Region}, se::Region)

Apply dilation to each region in regions with structuring element se, returning a new vector that contains only the non-empty results.

julia> using Regions

julia> regions = [region_from_box(-1, -1, 1, 1)];

julia> se = region_from_box(-1, -1, 1, 1);

julia> dilation(regions, se) == [region_from_box(-2, -2, 2, 2)]
true
source
Regions.erosionMethod
erosion(regions::Vector{Region}, se::Region)

Apply erosion to each region in regions with structuring element se, returning a new vector that contains only the non-empty results.

julia> using Regions

julia> regions = [region_from_box(-2, -2, 2, 2), Region([Run(0, 0:0)])];

julia> se = region_from_box(-1, -1, 1, 1);

julia> length(erosion(regions, se))
1
source
Regions.fill_holesMethod
fill_holes(regions::Vector{Region})

Apply fill_holes to each region in regions, returning a new vector with all holes filled.

julia> using Regions

julia> frame = difference(region_from_box(-3, -3, 3, 3), region_from_box(-1, -1, 1, 1));

julia> filled = fill_holes([frame]);

julia> contains(filled[1], 0, 0)
true
source
Regions.inner_boundaryMethod
inner_boundary(regions::Vector{Region})

Apply inner_boundary to each region in regions, returning a new vector of non-empty results.

julia> using Regions

julia> box = region_from_box(-2, -2, 2, 2);

julia> ib = inner_boundary([box]);

julia> contains(ib[1], -2, 0) && !contains(ib[1], 0, 0)
true
source
Regions.leftMethod
left(x::Vector{Region})

Calculates the leftmost coordinate of a vector of regions. Returns missing for an empty vector.

julia> using Regions

julia> left([Region([Run(2, 1:3)]), Region([Run(7, 1:3)])])
2

julia> ismissing(left(Region[]))
true
source
Regions.morphological_gradientMethod
morphological_gradient(regions::Vector{Region}, se::Region)

Apply morphological_gradient to each region in regions with structuring element se, returning a new vector of non-empty results.

julia> using Regions

julia> box = region_from_box(-2, -2, 2, 2);

julia> se  = region_from_box(-1, -1, 1, 1);

julia> grad = morphological_gradient([box], se);

julia> contains(grad[1], -2, 0) && !contains(grad[1], 0, 0)
true
source
Regions.openingMethod
opening(regions::Vector{Region}, se::Region)

Apply opening to each region in regions with structuring element se, returning a new vector that contains only the non-empty results.

julia> using Regions

julia> large = region_from_box(-2, -2, 2, 2);

julia> pixel = Region([Run(0, 0:0)]);

julia> se    = region_from_box(-1, -1, 1, 1);

julia> length(opening([large, pixel], se))
1
source
Regions.outer_boundaryMethod
outer_boundary(regions::Vector{Region})

Apply outer_boundary to each region in regions, returning a new vector of non-empty results.

julia> using Regions

julia> box = region_from_box(-2, -2, 2, 2);

julia> ob = outer_boundary([box]);

julia> contains(ob[1], -3, 0) && !contains(ob[1], -2, 0)
true
source
Regions.regions_to_imageFunction
regions_to_image(regions::Vector{Region}, colors=[Gray(true)])

Render a vector of regions to a single 2D Array sized to the combined bounding box. Each region is drawn in turn, taking its color from colors; when there are more regions than colors, the color list is cycled. Where two regions overlap, the previously drawn pixel is alpha-composited under the new pixel — pass colors with < 1 alpha (e.g. RGBA) to make overlaps visible.

This is the natural rendering for the output of components, where each connected blob can be tinted differently.

Some examples of color lists you can pass:

  • [Gray(0.5)] — flat mid gray for every region
  • [RGB(1, 0, 0), RGB(0, 1, 0), RGB(0, 0, 1)] — cycle red/green/blue per region
  • [RGBA(0, 0.5, 0, 0.5), RGBA(0.5, 0, 0, 0.5)] — half-transparent green and red

Compare with region_to_image, which renders a single Region with a single color.

source
Regions.rightMethod
right(x::Vector{Region})

Calculates the rightmost coordinate of a vector of regions. Returns missing for an empty vector.

julia> using Regions

julia> right([Region([Run(2, 1:3)]), Region([Run(7, 1:3)])])
7

julia> ismissing(right(Region[]))
true
source
Regions.topMethod
top(x::Vector{Region})

Calculates the minimum row coordinate across a vector of regions — the topmost row under the package's image-coordinate convention. Returns missing for an empty vector.

julia> using Regions

julia> top([Region([Run(2, 1:3)]), Region([Run(7, 2:6)])])
1

julia> ismissing(top(Region[]))
true
source
Regions.RegionsModule
Regions

Main module for Regions.jl - a set of types that model a discrete 2-dimensional region concept.

Exports

  • Run
  • Region
  • binarize
  • components

Dependencies

  • Images.jl
source
ImageBinarization.BinarizationAPI.binarizeMethod
binarize(image, predicate::Function)

Extend Images.binarize with a predicate-based method that returns a Region.

Scans image column by column and includes every pixel for which predicate returns true. Because the second argument is typed ::Function, Julia's dispatch selects this method when a plain function or lambda is passed, while algorithm-based calls such as binarize(img, Otsu()) continue to resolve to the Images.binarize method unchanged.

For images with at least 1024 columns and multiple threads available, the work is distributed across threads (one per column). Smaller images use a single-threaded path to avoid thread overhead.

reg = binarize(img, x -> x > 0.3)        # all pixels above 30 % brightness
reg = binarize(img, x -> x <= 0.5)       # all pixels at most 50 % brightness
reg = binarize(img, x -> 0.3 < x < 0.8) # pixels in the 30–80 % range
source
Regions.componentsFunction
components(region::Region, dx::Unsigned=1, dy::Unsigned=1)

Split region into its connected components and return them as a Vector{Region} — one element per blob.

Two runs are considered part of the same component when their column distance is at most dx and their row gap is at most dy. The defaults dx = dy = 1 give standard 8-connected labelling; larger values intentionally bridge small gaps between nearby objects (useful for joining slightly-fragmented blobs into a single component before measurement).

Because the algorithm walks the run list rather than the pixel grid, it scales as O(n_runs) — independent of image area. On sparse industrial images this is typically orders of magnitude faster than pixel-based connected-component labelling.

julia> using Regions

julia> b1 = region_from_circle(-20, 0, 5);

julia> b2 = region_from_circle(  0, 0, 5);

julia> b3 = region_from_circle( 20, 0, 5);

julia> length(components(union(union(b1, b2), b3)))
3
source

Morphological Operations

Regions._minkowski_additionMethod
_minkowski_addition(a::Region, b::Region)

Compute the Minkowski sum of two regions, with complement handling via DeMorgan's rules. Both arguments being complements is not supported.

source
Regions._minkowski_subtractionMethod
_minkowski_subtraction(a::Region, b::Region)

Compute the Minkowski difference of two regions, with complement handling via DeMorgan's rules. The structuring element being a complement is not supported.

source
Regions.closingMethod
closing(a::Region, b::Region)

Morphological closing: dilation of a by b, followed by Minkowski subtraction with b.

Closing fills gaps and holes smaller than the structuring element and smoothes the region boundary. The result always contains a as a subset.

Structuring elements should be centred on the origin.

julia> using Regions

julia> gapped = Region([Run(-1, 0:0), Run(1, 0:0)]);

julia> se = region_from_box(-1, -1, 1, 1);

julia> c = closing(gapped, se);

julia> contains(c, 0, 0)
true

julia> contains(c, -1, 0) && contains(c, 1, 0)
true
source
Regions.dilationMethod
dilation(a::Region, b::Region)

Dilate region a with structuring element b.

Dilation grows a: the result contains every point reachable by placing b centred at any pixel of a. Dilation may merge disconnected parts.

Structuring elements should be centred on the origin. Complement regions are handled via DeMorgan's rules.

julia> using Regions

julia> small = region_from_box(-1, -1, 1, 1);

julia> se    = region_from_box(-1, -1, 1, 1);

julia> dilation(small, se) == region_from_box(-2, -2, 2, 2)
true
source
Regions.erosionMethod
erosion(a::Region, b::Region)

Erode region a with structuring element b.

Erosion shrinks a: the result contains every point c such that the reflected (inverted) b centred at c fits entirely within a. Erosion may split a connected region into disconnected parts.

Structuring elements should be centred on the origin. Complement regions are handled via DeMorgan's rules.

julia> using Regions

julia> big = region_from_box(-2, -2, 2, 2);

julia> se  = region_from_box(-1, -1, 1, 1);

julia> erosion(big, se) == se
true

julia> isempty(erosion(Region([Run(0, 0:0)]), se))
true
source
Regions.fill_holesMethod
fill_holes(region::Region)

Fill the holes of a region.

Returns a new region equal to region with all enclosed holes filled in. If the region has no holes the original region is returned unchanged.

Only non-complement regions are supported.

julia> using Regions

julia> frame = difference(region_from_box(-3, -3, 3, 3), region_from_box(-1, -1, 1, 1));

julia> filled = fill_holes(frame);

julia> contains(filled, 0, 0)
true

julia> contains(filled, -3, 0)
true
source
Regions.holesMethod
holes(region::Region)

Extract the holes of a region.

A hole is a connected component of the complement-within-bounding-box that does not touch the bounding box boundary. Returns a Vector{Region}, one element per hole.

Only non-complement regions are supported.

julia> using Regions

julia> frame = difference(region_from_box(-3, -3, 3, 3), region_from_box(-1, -1, 1, 1));

julia> hs = holes(frame);

julia> length(hs)
1

julia> contains(hs[1], 0, 0)
true
source
Regions.inner_boundaryMethod
inner_boundary(a::Region)

Compute the inner boundary of a region.

The inner boundary is the set of pixels that belong to a but would be removed by a 3×3 erosion — i.e. the outermost layer of pixels strictly inside a.

julia> using Regions

julia> box = region_from_box(-2, -2, 2, 2);

julia> ib = inner_boundary(box);

julia> contains(ib, -2, 0)
true

julia> contains(ib, 0, 0)
false
source
Regions.morphological_gradientMethod
morphological_gradient(a::Region, b::Region)

Morphological gradient: difference of the dilation and erosion of a by b.

The result is a ring around the boundary of a, lying partly inside and partly outside.

Structuring elements should be centred on the origin.

julia> using Regions

julia> box = region_from_box(-2, -2, 2, 2);

julia> se  = region_from_box(-1, -1, 1, 1);

julia> grad = morphological_gradient(box, se);

julia> contains(grad, -2, 0)
true

julia> contains(grad, 0, 0)
false
source
Regions.openingMethod
opening(a::Region, b::Region)

Morphological opening: erosion of a by b, followed by Minkowski addition with b.

Opening removes structures smaller than the structuring element and smoothes the region boundary. The result is always a subset of a.

Structuring elements should be centred on the origin.

julia> using Regions

julia> big = region_from_box(-2, -2, 2, 2);

julia> se  = region_from_box(-1, -1, 1, 1);

julia> opening(big, se) == big
true

julia> isempty(opening(Region([Run(0, 0:0)]), se))
true
source
Regions.outer_boundaryMethod
outer_boundary(a::Region)

Compute the outer boundary of a region.

The outer boundary is the set of pixels that do not belong to a but are added by a 3×3 dilation — i.e. the innermost layer of pixels strictly outside a.

julia> using Regions

julia> box = region_from_box(-2, -2, 2, 2);

julia> ob = outer_boundary(box);

julia> contains(ob, -3, 0)
true

julia> contains(ob, -2, 0)
false
source

Blob Analysis Features

Regions._raw_momentsMethod
_raw_moments(r::Region) -> NamedTuple

Compute all 10 raw spatial moments in a single pass over runs. Column = x, row = y. Returns (m00, m10, m01, m20, m11, m02, m30, m21, m12, m03).

source
Regions.areaMethod
area(r::Region) -> Int

Return the number of pixels in a non-complement region.

julia> using Regions

julia> area(Region([Run(0, -1:1), Run(1, -1:1), Run(2, -1:1)]))
9

julia> area(Region([Run(2, 3:3)]))
1
source
Regions.area_of_holesMethod
area_of_holes(r::Region) -> Int

Return the total pixel count of all holes in the region.

julia> using Regions

julia> r = Region([Run(c, 0:4) for c in 0:4]);   # solid 5×5 square — no holes

julia> area_of_holes(r)
0
source
Regions.aspect_ratioMethod
aspect_ratio(r::Region) -> Float64

Return width / height of the bounding box.

julia> using Regions

julia> aspect_ratio(Region([Run(0, 0:3), Run(1, 0:3)]))
0.5
source
Regions.bounds_centerMethod
bounds_center(r::Region) -> Tuple{Float64,Float64}

Return the center of the bounding box as (column, row).

julia> using Regions

julia> bounds_center(Region([Run(0, -1:1), Run(1, -1:1), Run(2, -1:1)]))
(1.0, 0.0)
source
Regions.centroidMethod
centroid(r::Region) -> Tuple{Float64,Float64}

Return the centroid (column, row) of the region.

julia> using Regions

julia> centroid(Region([Run(0, -1:1), Run(1, -1:1), Run(2, -1:1)]))
(1.0, 0.0)

julia> centroid(Region([Run(2, 3:3)]))
(2.0, 3.0)
source
Regions.compactnessMethod
compactness(r::Region) -> Float64

Return the isoperimetric ratio perimeter² / (4π·area). Equals 1 for a circle, greater than 1 for less compact shapes.

julia> using Regions

julia> r = region_from_circle(0, 0, 50);

julia> compactness(r) < 1.7
true
source
Regions.contourMethod
contour(r::Region) -> PointList

Return the first (outer) boundary polygon of r as a PointList. Equivalent to vectorized_boundaries(r)[1].

julia> using Regions

julia> length(contour(Region([Run(0, 0:0)])))
4
source
Regions.convex_areaMethod
convex_area(r::Region) -> Float64

Return the area of the convex hull (in pixel units).

julia> using Regions

julia> convex_area(Region([Run(0, 0:1), Run(1, 0:1)]))
4.0
source
Regions.convex_hullMethod
convex_hull(r::Region) -> Vector{Tuple{Float64,Float64}}

Return the convex hull of the region as an ordered list of vertices (CCW). Coordinates are half-integer pixel corners.

julia> using Regions

julia> h = convex_hull(Region([Run(0, 0:1), Run(1, 0:1)]));

julia> length(h)
4
source
Regions.convex_perimeterMethod
convex_perimeter(r::Region) -> Float64

Return the perimeter of the convex hull.

julia> using Regions

julia> convex_perimeter(Region([Run(0, 0:1), Run(1, 0:1)]))
8.0
source
Regions.convexityMethod
convexity(r::Region) -> Float64

Return area / convex_area. Equals 1 for convex regions, less than 1 for concave ones.

julia> using Regions

julia> convexity(Region([Run(0, 0:1), Run(1, 0:1)]))
1.0
source
Regions.equivalent_ellipseMethod
equivalent_ellipse(r::Region) -> NamedTuple

Return the equivalent ellipse of a region as a named tuple (center, semi_axes, angle), where:

  • center is a Tuple{Float64,Float64} (column, row),
  • semi_axes is a Tuple{Float64,Float64} (major, minor),
  • angle is in radians, measured from the column (x) axis towards the row (y) axis.
julia> using Regions

julia> e = equivalent_ellipse(Region([Run(0, -1:1), Run(1, -1:1), Run(2, -1:1)]));

julia> e.center
(1.0, 0.0)

julia> round(e.angle; digits=4)
0.0
source
Regions.feret_diametersMethod
feret_diameters(r::Region) -> Tuple{Float64,Float64}

Return (min_feret, max_feret) — the minimum and maximum caliper widths — computed via rotating calipers on the convex hull.

julia> using Regions

julia> mn, mx = feret_diameters(Region([Run(0, 0:0)]));

julia> mn ≈ 1.0 && mx ≈ sqrt(2)
true
source
Regions.heightMethod
height(r::Region) -> Int

Return the height (row extent) of the bounding box of a non-empty, non-complement region.

julia> using Regions

julia> height(Region([Run(0, -1:1), Run(1, -1:1), Run(2, -1:1)]))
3
source
Regions.momentsMethod
moments(r::Region) -> NamedTuple

Return all 10 raw spatial moments of the region as a named tuple (m00, m10, m01, m20, m11, m02, m30, m21, m12, m03).

Column is the x-axis, row is the y-axis.

julia> using Regions

julia> m = moments(Region([Run(2, 3:3)]))
(m00 = 1.0, m10 = 2.0, m01 = 3.0, m20 = 4.0, m11 = 6.0, m02 = 9.0, m30 = 8.0, m21 = 12.0, m12 = 18.0, m03 = 27.0)

julia> m.m00
1.0
source
Regions.number_of_holesMethod
number_of_holes(r::Region) -> Int

Return the number of holes (connected background components fully enclosed by the region).

julia> using Regions

julia> r = Region([Run(c, 0:4) for c in 0:4]);   # solid 5×5 square — no holes

julia> number_of_holes(r)
0
source
Regions.perforationMethod
perforation(r::Region) -> Float64

Return (convex_area - area) / convex_area.

julia> using Regions

julia> perforation(Region([Run(0, 0:1), Run(1, 0:1)]))
0.0
source
Regions.perimeterMethod
perimeter(r::Region) -> Float64

Return the perimeter of a region measured as the number of 4-connected boundary edges (each edge has length 1.0).

julia> using Regions

julia> perimeter(Region([Run(0, 0:0)]))   # single pixel: 4 exposed edges
4.0

julia> perimeter(Region([Run(0, 0:1), Run(1, 0:1)]))   # 2×2 square: 8 edges
8.0
source
Regions.to_point_listMethod
to_point_list(r::Region) -> PointList

Flatten all boundary polygons of r into a single PointList. Equivalent to concatenating all polygons returned by vectorized_boundaries.

julia> using Regions

julia> length(to_point_list(Region([Run(0, 0:0)])))
4
source
Regions.vectorized_boundariesMethod
vectorized_boundaries(r::Region) -> Vector{PointList}

Convert a region to a list of closed boundary polygons. Outer boundaries wind clockwise; hole boundaries wind counter-clockwise (interior always to the right of travel direction). Coordinates use pixel-corner convention: a pixel at (col, row) has its corners at (col±0.5, row±0.5).

The result may contain collinear midpoints on straight edges; call remove_collinear(poly) on each polygon to simplify.

julia> using Regions

julia> polys = vectorized_boundaries(Region([Run(0, 0:0)]));

julia> length(polys)
1

julia> length(polys[1])
4
source
Regions.widthMethod
width(r::Region) -> Int

Return the width (column extent) of the bounding box of a non-empty, non-complement region.

julia> using Regions

julia> width(Region([Run(0, -1:1), Run(1, -1:1), Run(2, -1:1)]))
3
source

Point List

Regions.areaMethod
area(pts::PointList) -> Float64

Return the signed area of a polygon using the shoelace formula. Positive for counter-clockwise winding, negative for clockwise.

julia> using Regions

julia> area([(0.0,0.0),(1.0,0.0),(1.0,1.0),(0.0,1.0)])
1.0
source
Regions.bounding_boxMethod
bounding_box(pts::PointList) -> NamedTuple

Return the axis-aligned bounding box of a point list as (xmin, xmax, ymin, ymax).

julia> using Regions

julia> bb = bounding_box([(1.0,2.0),(3.0,4.0),(2.0,1.0)]);

julia> bb.xmin, bb.xmax, bb.ymin, bb.ymax
(1.0, 3.0, 1.0, 4.0)
source
Regions.centroidMethod
centroid(pts::PointList) -> Tuple{Float64,Float64}

Return the centroid (arithmetic mean) of the points as (column, row).

julia> using Regions

julia> centroid([(0.0,0.0),(2.0,0.0),(2.0,2.0),(0.0,2.0)])
(1.0, 1.0)
source
Regions.convex_hullMethod
convex_hull(pts::PointList) -> PointList

Return the convex hull of a point list as CCW-ordered vertices, using Andrew's monotone chain algorithm.

julia> using Regions

julia> pts = [(0.0,0.0),(1.0,0.0),(1.0,1.0),(0.0,1.0),(0.5,0.5)];

julia> h = convex_hull(pts);

julia> length(h)
4
source
Regions.convex_hull_cwMethod
convex_hull_cw(pts::PointList) -> PointList

Return the convex hull of a point list in clockwise order.

julia> using Regions

julia> pts = [(0.0,0.0),(1.0,0.0),(1.0,1.0),(0.0,1.0)];

julia> convex_hull_cw(pts) == reverse(convex_hull(pts))
true
source
Regions.feret_diametersMethod
feret_diameters(pts::PointList) -> Tuple{Float64,Float64}

Return (min_feret, max_feret) — minimum and maximum caliper widths — computed via rotating calipers on the convex hull of pts.

julia> using Regions

julia> mn, mx = feret_diameters([(0.0,0.0),(1.0,0.0),(1.0,1.0),(0.0,1.0)]);

julia> mn ≈ 1.0 && mx ≈ sqrt(2)
true
source
Regions.maximum_widthMethod
maximum_width(pts::PointList) -> Float64

Return the maximum caliper width (maximum Feret diameter) of a point list.

julia> using Regions

julia> maximum_width([(0.0,0.0),(1.0,0.0),(1.0,1.0),(0.0,1.0)]) ≈ sqrt(2)
true
source
Regions.minimum_area_bounding_rectangleMethod
minimum_area_bounding_rectangle(pts::PointList) -> NamedTuple

Return the minimum-area bounding rectangle via rotating calipers on the convex hull. Returns (corners, width, height, angle, area) where corners is a PointList of 4 CCW vertices and angle is the long-axis rotation in radians.

julia> using Regions

julia> r = minimum_area_bounding_rectangle([(0.0,0.0),(2.0,0.0),(2.0,1.0),(0.0,1.0)]);

julia> r.area ≈ 2.0
true
source
Regions.minimum_bounding_circleMethod
minimum_bounding_circle(pts::PointList) -> NamedTuple

Return the minimum enclosing circle of a point list as (center, radius). Uses Welzl's incremental algorithm.

julia> using Regions

julia> c = minimum_bounding_circle([(0.0,0.0),(2.0,0.0),(2.0,2.0),(0.0,2.0)]);

julia> c.center[1] ≈ 1.0 && c.center[2] ≈ 1.0 && c.radius ≈ sqrt(2)
true
source
Regions.minimum_perimeter_bounding_rectangleMethod
minimum_perimeter_bounding_rectangle(pts::PointList) -> NamedTuple

Return the minimum-perimeter bounding rectangle via rotating calipers on the convex hull. Returns (corners, width, height, angle, perimeter) where corners is a PointList of 4 CCW vertices and angle is the long-axis rotation in radians.

julia> using Regions

julia> r = minimum_perimeter_bounding_rectangle([(0.0,0.0),(2.0,0.0),(2.0,1.0),(0.0,1.0)]);

julia> r.perimeter ≈ 6.0
true
source
Regions.minimum_widthMethod
minimum_width(pts::PointList) -> Float64

Return the minimum caliper width (minimum Feret diameter) of a point list.

julia> using Regions

julia> minimum_width([(0.0,0.0),(1.0,0.0),(1.0,1.0),(0.0,1.0)]) ≈ 1.0
true
source
Regions.perimeterMethod
perimeter(pts::PointList) -> Float64

Return the perimeter of a closed polygon. Points are first simplified using simplify_radial_distance with tolerance 3 to reduce the effect of digitisation jaggies on oblique edges.

julia> using Regions

julia> pts = [(0.0,0.0),(100.0,0.0),(100.0,100.0),(0.0,100.0)];

julia> perimeter(pts) ≈ 400.0
true
source
Regions.point_atMethod
point_at(pts::PointList, distance::Real) -> Tuple{Float64,Float64}

Return the point at arc-length distance along the polyline.

  • distance = 0: first point.
  • 0 < distance < total_length: interpolated point on the appropriate segment.
  • distance < 0 or distance > total_length: extrapolated beyond the respective end in the direction of the first or last edge.
julia> using Regions

julia> pts = [(0.0,0.0),(1.0,0.0),(1.0,1.0)];

julia> point_at(pts, 1.5)
(1.0, 0.5)
source
Regions.remove_collinearMethod
remove_collinear(pts::PointList) -> PointList

Remove collinear consecutive vertices from a polygon. A vertex is removed if it lies exactly on the line through its two neighbours.

julia> using Regions

julia> pts = [(0.0,0.0),(0.5,0.0),(1.0,0.0),(1.0,1.0),(0.0,1.0)];

julia> remove_collinear(pts)
4-element Vector{Tuple{Float64, Float64}}:
 (0.0, 0.0)
 (1.0, 0.0)
 (1.0, 1.0)
 (0.0, 1.0)
source
Regions.simplified_perimeterMethod
simplified_perimeter(pts::PointList, tolerance::Real) -> Float64

Return the perimeter of a closed polygon after simplifying with the given tolerance (see simplify_radial_distance).

julia> using Regions

julia> pts = [(0.0,0.0),(100.0,0.0),(100.0,100.0),(0.0,100.0)];

julia> simplified_perimeter(pts, 3.0) ≈ 400.0
true
source
Regions.simplify_radial_distanceFunction
simplify_radial_distance(pts::PointList, tolerance::Real, closed::Bool=false) -> PointList

Simplify a polyline by removing points that lie within tolerance of the previous kept point (radial distance algorithm).

If closed is true the polyline is treated as a closed polygon: the first point is appended at the end of the result so that the closing edge is included when summing edge lengths.

julia> using Regions

julia> pts = [(0.0,0.0),(0.5,0.0),(100.0,0.0),(100.0,100.0)];

julia> length(simplify_radial_distance(pts, 1.0))
3
source
Regions.translateMethod
translate(pts::PointList, dx::Real, dy::Real) -> PointList
translate(pts::PointList, d::Vector) -> PointList

Translate a point list by (dx, dy).

julia> using Regions

julia> translate([(0.0,0.0),(1.0,0.0)], 2.0, 3.0)
2-element Vector{Tuple{Float64, Float64}}:
 (2.0, 3.0)
 (3.0, 3.0)
source

Index