Skip to content

Polygons

Abstract polygons

In this package, any polygon regardless of its concrete representation in memory should be a subtype of Devices.AbstractPolygon.

# Devices.AbstractPolygonType.

abstract type AbstractPolygon{T<:Coordinate} end

Anything you could call a polygon regardless of the underlying representation. Currently only Rectangle or Polygon are concrete subtypes, but one could imagine further subtypes to represent specific shapes that appear in highly optimized pattern formats. Examples include the OASIS format (which has 25 implementations of trapezoids) or e-beam lithography pattern files like the Raith GPF format.

source

Affine transformations

The mechanism for affine transformations is largely provided by the CoordinateTransformations.jl package. For convenience, the documentation for Translation and compose is reproduced below from that package. We implement our own 2D rotations.

An example of how to use affine transformations with polygons:

julia> r = Rectangle(1,1)
Rectangle{Int64}((0,0), (1,1))

julia> trans = Translation(10,10)
Translation(10, 10)

julia> trans = Rotation(90°)  trans
AffineMap([0.0 -1.0; 1.0 0.0], [-10.0, 10.0])

julia> trans(r)
Polygon{Float64}(Point{Float64}[(-10.0,10.0), (-10.0,11.0), (-11.0,11.0), (-11.0,10.0)])

# CoordinateTransformations.composeFunction.

compose(trans1, trans2)
trans1  trans2

Take two transformations and create a new transformation that is equivalent to successively applying trans2 to the coordinate, and then trans1. By default will create a ComposedTransformation, however this method can be overloaded for efficiency (e.g. two affine transformations naturally compose to a single affine transformation).

# Devices.Points.RotationFunction.

Rotation(Θ)

Construct a rotation about the origin. Units accepted (no units ⇒ radians).

source

# CoordinateTransformations.TranslationType.

Translation(v) <: AbstractAffineMap
Translation(dx, dy)       (2D)
Translation(dx, dy, dz)   (3D)

Construct the Translation transformation for translating Cartesian points by an offset v = (dx, dy, ...)

# Devices.Points.XReflectionFunction.

XReflection()

Construct a reflection about the x-axis (y-coordinate changes sign).

Example:

julia> trans = XReflection()
LinearMap([1 0; 0 -1])

julia> trans(Point(1,1))
2-element Point{Int64}:
  1
 -1

source

# Devices.Points.YReflectionFunction.

YReflection()

Construct a reflection about the y-axis (x-coordinate changes sign).

Example:

julia> trans = YReflection()
LinearMap([-1 0; 0 1])

julia> trans(Point(1,1))
2-element Point{Int64}:
 -1
  1

source

Clipping

# Devices.Polygons.clipFunction.

clip(op::Clipper.ClipType, s::AbstractPolygon{S}, c::AbstractPolygon{T};
    pfs::Clipper.PolyFillType=Clipper.PolyFillTypeEvenOdd,
    pfc::Clipper.PolyFillType=Clipper.PolyFillTypeEvenOdd) where
    {S <: Coordinate,T <: Coordinate}
clip(op::Clipper.ClipType, s::AbstractVector{A}, c::AbstractVector{B};
    pfs::Clipper.PolyFillType=Clipper.PolyFillTypeEvenOdd,
    pfc::Clipper.PolyFillType=Clipper.PolyFillTypeEvenOdd) where
    {S, T, A <: AbstractPolygon{S}, B <: AbstractPolygon{T}}
clip{S<:AbstractPolygon, T<:AbstractPolygon}(op::Clipper.ClipType,
    s::AbstractVector{S}, c::AbstractVector{T};
    pfs::Clipper.PolyFillType=Clipper.PolyFillTypeEvenOdd,
    pfc::Clipper.PolyFillType=Clipper.PolyFillTypeEvenOdd)

Using the Clipper library and the Clipper.jl wrapper, perform polygon clipping. The first argument must be one of the following types :

  • Clipper.ClipTypeDifference
  • Clipper.ClipTypeIntersection
  • Clipper.ClipTypeUnion
  • Clipper.ClipTypeXor

Note that these are types; you should not follow them with (). The second and third arguments are AbstractPolygon objects. Keyword arguments pfs and pfc specify polygon fill rules (see the Clipper docs for further information). These arguments may include:

  • Clipper.PolyFillTypeNegative
  • Clipper.PolyFillTypePositive
  • Clipper.PolyFillTypeEvenOdd
  • Clipper.PolyFillTypeNonZero

source

Offsetting

# Devices.Polygons.offsetFunction.

offset{S<:Coordinate}(s::AbstractPolygon{S}, delta::Coordinate;
    j::Clipper.JoinType=Clipper.JoinTypeMiter,
    e::Clipper.EndType=Clipper.EndTypeClosedPolygon)
offset{S<:AbstractPolygon}(subject::AbstractVector{S}, delta::Coordinate;
    j::Clipper.JoinType=Clipper.JoinTypeMiter,
    e::Clipper.EndType=Clipper.EndTypeClosedPolygon)
offset{S<:Polygon}(s::AbstractVector{S}, delta::Coordinate;
    j::Clipper.JoinType=Clipper.JoinTypeMiter,
    e::Clipper.EndType=Clipper.EndTypeClosedPolygon)

Using the Clipper library and the Clipper.jl wrapper, perform polygon offsetting.

The first argument should be an AbstractPolygon. The second argument is how much to offset the polygon. Keyword arguments include a join type:

  • Clipper.JoinTypeMiter
  • Clipper.JoinTypeRound
  • Clipper.JoinTypeSquare

and also an end type:

  • Clipper.EndTypeClosedPolygon
  • Clipper.EndTypeClosedLine
  • Clipper.EndTypeOpenSquare
  • Clipper.EndTypeOpenRound
  • Clipper.EndTypeOpenButt

source

Rectangle API

# Devices.Rectangles.RectangleType.

struct Rectangle{T} <: AbstractPolygon{T}
    ll::Point{T}
    ur::Point{T}
    function Rectangle(a,b)
        # Ensure ll is lower-left, ur is upper-right.
        ll = Point(a.<=b) .* a + Point(b.<=a) .* b
        ur = Point(a.<=b) .* b + Point(b.<=a) .* a
        new(ll,ur)
    end
end

A rectangle, defined by opposing lower-left and upper-right corner coordinates. Lower-left and upper-right are guaranteed to be such by the inner constructor.

source

# Devices.Rectangles.RectangleMethod.

Rectangle(ll::Point, ur::Point)

Convenience constructor for Rectangle objects.

source

# Devices.Rectangles.RectangleMethod.

Rectangle(width, height)

Constructs Rectangle objects by specifying the width and height rather than the lower-left and upper-right corners.

The rectangle will sit with the lower-left corner at the origin. With centered rectangles we would need to divide width and height by 2 to properly position. If we wanted an object of Rectangle{Int} type, this would not be possible if either width or height were odd numbers. This definition ensures type stability in the constructor.

source

# Devices.boundsMethod.

bounds(r::Rectangle)

No-op (just returns r).

source

# Devices.centerMethod.

center(r::Rectangle)

Returns a Point corresponding to the center of the rectangle.

source

# Devices.centeredMethod.

centered(r::Rectangle)

Centers a copy of r, with promoted coordinates if necessary. This function will not throw an InexactError(), even if r had integer coordinates.

source

# Devices.Rectangles.heightMethod.

height(r::Rectangle)

Return the height of a rectangle.

source

# Devices.Rectangles.isproperMethod.

isproper(r::Rectangle)

Returns true if the rectangle has a non-zero area. Otherwise, returns false. Note that the upper-right and lower-left corners are enforced to be the ur and ll fields of a Rectangle by the inner constructor.

source

# Devices.lowerleftMethod.

lowerleft(r::Rectangle)

Returns the lower-left corner of a rectangle (Point object).

source

# Devices.upperrightMethod.

upperright(r::Rectangle)

Returns the upper-right corner of a rectangle (Point object).

source

# Devices.Polygons.pointsMethod.

points{T}(x::Rectangle{T})

Returns the array of Point objects defining the rectangle.

source

# Devices.Rectangles.widthMethod.

width(r::Rectangle)

Return the width of a rectangle.

source

# Base.:+Method.

+(r::Rectangle, p::Point)

Translate a rectangle by p.

source

Polygon API

# Devices.Polygons.PolygonType.

struct Polygon{T} <: AbstractPolygon{T}
    p::Vector{Point{T}}
    Polygon(x) = new(x)
    Polygon(x::AbstractPolygon) = convert(Polygon{T}, x)
end

Polygon defined by list of coordinates. The first point should not be repeated at the end (although this is true for the GDS format).

source

# Devices.Polygons.PolygonMethod.

Polygon{T}(parr::AbstractVector{Point{T}})

Convenience constructor for a Polygon{T} object.

source

# Devices.Polygons.PolygonMethod.

Polygon(p0::Point, p1::Point, p2::Point, p3::Point...)

Convenience constructor for a Polygon{T} object.

source

# Devices.boundsMethod.

bounds(p::Polygon)

Return a bounding Rectangle for polygon p.

source

# Devices.boundsMethod.

bounds(parr::AbstractArray{<:AbstractPolygon})

Return a bounding Rectangle for an array parr of AbstractPolygon objects. Rectangles having zero width and height should be excluded from the calculation.

source

# Devices.boundsMethod.

bounds(p0::AbstractPolygon, p::AbstractPolygon...)

Return a bounding Rectangle for several AbstractPolygon objects.

source

# Devices.lowerleftMethod.

lowerleft(x::Polygon)

Return the lower-left-most corner of a rectangle bounding polygon x. Note that this point doesn't have to be in the polygon.

source

# Devices.upperrightMethod.

upperright(x::Polygon)

Return the upper-right-most corner of a rectangle bounding polygon x. Note that this point doesn't have to be in the polygon.

source

# Devices.Polygons.pointsMethod.

points(x::Polygon)

Returns the array of Point objects defining the polygon.

source