2D Geometry

We provide basic functionality for two-dimensional geometry over the rational numbers. This includes distances and angles, Graham scan for computing the convex hull, intersection of lines, and affine halfplanes.

Points

In RationalPolygons.jl, we represent points as static vectors of length two. As the name suggests, these are statically sized, which leads to improved performance and memory management for many common operations. We provide three type aliases LatticePoint, RationalPoint, and Point, the latter being the union of the previous two. Note that everything is stated for an arbitrary subtype T <: Integer, e.g. fixed size machine integers like Int64, or unbounded integer types like BigInt.

RationalPolygons.is_k_rationalMethod
is_k_rational(k :: T, p :: Point{T}) where {T <: Integer}

Checks whether a point p is k-rational, i.e. its coordinates have denominator at most k.

source
Base.denominatorMethod
denominator(p :: Point)

The smallest integer r such that r*p is integral.

Example

julia> denominator(RationalPoint(1//2,1//3))
6
source
RationalPolygons.multiplicityMethod
multiplicity(p :: Point)

The unique rational number x such that x*p is primitive and integral.

Example

julia> multiplicity(RationalPoint(4//3,2//3))
3//2
source
RationalPolygons.primitivizeMethod
primitivize(p :: Point)

Return the unique primitive lattice point on the ray spanned by p.

Example

julia> primitivize(RationalPoint(4//3,2//3))
2-element StaticArraysCore.SVector{2, Int64} with indices SOneTo(2):
 2
 1
source
RationalPolygons.normFunction
norm(p :: Point{T})

Return the square of the euclidean norm of p.

Example

julia> norm(RationalPoint(4//3,2//3))
20//9
source
RationalPolygons.pseudo_angleMethod
pseudo_angle(p :: Point{T}) where {T <: Integer}

Returns a value in the half-open interval $(-2,2]$. A pseudo angle allows comparing vectors by angle, but is faster to compute than the euclidean angle.

Example

julia> pseudo_angle(LatticePoint(1,1))
1//2
source

Graham scan

The Graham scan is a planar convex hull algorithm named after Ronald Graham [1]. With an asymptotic running time of $O(n \cdot \mathrm{log}(n))$, it is a lot quicker than algorithms that work in arbitrary dimension.

RationalPolygons.graham_scan!Function
graham_scan!(points :: Vector{<:Point{T}}) where {T <: Integer}

Perform a Graham scan on the given points, removing all points that are not vertices of their convex hull and ordering them counterclockwise.

Example

julia> points = LatticePoint{Int}[(0,0),(1,0),(1,1),(0,1),(-1,1),(0,-1),(-1,-1),(0,-1)];

julia> graham_scan!(points)
5-element Vector{StaticArraysCore.SVector{2, Int64}}:
 [-1, -1]
 [0, -1]
 [1, 0]
 [1, 1]
 [-1, 1]
source

Lines

We provide basic functionality for lines in the two-dimensional rational plane. A line is encoded as a simple struct consisting of a base point and a direction vector. Two lines can be intersected to produce a value of type IntersectionBehaviour. This is an abstract type with three concrete subtypes NoIntersection, LinesAreEqual and IntersectInPoint, capturing the three intersection scenarios.

RationalPolygons.LineType
Line{T <: Integer}

A line in 2-dimensional rational space. It has two fields: base_point :: RationalPoint{T} and direction_vector :: RationalPoint{T}.

source
RationalPolygons.line_through_pointsFunction
line_through_points(A :: Point{T}, B :: Point{T}) where {T <: Integer}

Return the line going through the points A and B.

Example:

julia> line_through_points(Point(1,0),Point(2,2))
Line with base point Rational{Int64}[1, 0] and direction vector Rational{Int64}[1, 2]
source
Base.inMethod
Base.in(x :: Point{T}, L :: Line{T}) where {T <: Integer}

Check whether a point x lies on a line L.

Example:

julia> RationalPoint(3//2,1) ∈ line_through_points(Point(1,0),Point(2,2))
true
source
RationalPolygons.normal_vectorMethod
normal_vector(L :: Line{T}) where {T <: Integer}

Return a primitive vector orthogonal to the direction vector of L.

Example:

julia> normal_vector(line_through_points(Point(1,0),Point(2,2)))
2-element StaticArraysCore.SVector{2, Int64} with indices SOneTo(2):
 -2
  1
source
RationalPolygons.IntersectInPointType
IntersectInPoint{T <: Integer}

The intersection behaviour of two lines intersecting in a unique point. This struct has a single field p, which is the intersection point.

source
RationalPolygons.intersection_behaviourFunction
intersection_behaviour(L1 :: Line{T}, L2 :: Line{T}) where {T <: Integer}

Given two lines in 2D rational space, return the intersection behaviour of the two lines: Possible values are LinesAreEqual(), NoIntersection() and IntersectInPoint(p) where p is the unique intersection point.

source
RationalPolygons.intersection_pointFunction
intersection_point(L1 :: Line{T}, L2 :: Line{T}) where {T <: RationalUnion}

Return the intersection point of two lines in 2D rational space. Throws an error if the lines do not intersect uniquely.

source

Affine halfplanes

An affine halfplane is encoded as a struct consisting of a normal vector $v \in \mathbb{Q}^2$ and a translation $b \in \mathbb{Q}^2$, representing the set of points $\{ x \in \mathbb{Q}^2 | \langle v, x \rangle \geq b \}$.

RationalPolygons.AffineHalfplaneType
AffineHalfplane{T <: Integer}

An affine halfplane in two-dimensional rational space. It has two fields normal_vector :: RationalPoint{T} and translation :: Rational{T}.

source
RationalPolygons.affine_halfplaneFunction
affine_halfplane(nv :: Point{T}, b :: Union{T, Rational{T}}) where {T <: Integer}

Return the affine halfplane given by the equation nv[1] * x[1] + nv[2] * x[2] ≥ b.

Example:

julia> affine_halfplane(Point(-2,1),1)
Affine halfplane given by -2//1 x + 1//1 y ≥ 1//1
source
affine_halfplane(L :: Line{T}) where {T <: Integer}

Return the affine halfplane associated to a line. The halfplane is understood to consist of those points to the left of the line L, looking in the direction given by direction_vector(L).

source
affine_halfplane(p :: Point{T}, q :: Point{T}) where {T <: Integer}

Return the affine halfplane associated to the line going through the points p and q.

source
affine_halfplane(P :: RationalPolygon, i :: Int)

Return the i-th describing halfplane of P.

source
Base.inMethod
Base.in(x :: Point{T}, H :: AffineHalfplane{T}) where {T <: Integer}

Check whether a point x lies in the halfplane H.

Example:

julia> Point(0,1) ∈ affine_halfplane(Point(-2,1),1)
true
source
RationalPolygons.contains_in_interiorMethod
contains_in_interior(x :: Point{T}, H :: AffineHalfplane{T}) where {T <: Integer}

Check whether a point x lies in the interior of H.

Example:

julia> contains_in_interior(Point(0,1), affine_halfplane(Point(-2,1),1))
false
source
Base.issubsetMethod
Base.issubset(H1 :: AffineHalfplane{T}, H2 :: AffineHalfplane{T}) where {T <: Integer}

Check whether H1 is a subset of H2.

source