Cells
Cells are used to logically group polygons or references to other cells into a single entity. They can contain references to other cells or arrays of other cells. They also store polygons. Here is the definition of a Cell
:
#
Devices.Cells.Cell
— Type.
mutable struct Cell{S<:Coordinate, T<:Meta} name::String elements::Vector{CellPolygon{S,T}} refs::Vector{CellRef} create::DateTime Cell(x,y,z,t) = new(x, y, z, t) Cell(x,y,z) = new(x, y, z, now()) Cell(x,y) = new(x, y, CellRef[], now()) Cell(x) = new(x, Polygon{T}[], CellRef[], now()) Cell() = begin c = new() c.elements = Polygon{T}[] c.refs = CellRef[] c.create = now() c end end
A cell has a name and contains polygons and references to CellArray
or CellReference
objects. It also records the time of its own creation. As currently implemented it mirrors the notion of cells in GDS-II files.
To add elements, use render!
. To add references, push them to refs
field.
The type parameter S
of a Cell{S,T}
object is used in two ways:
- Determine the units of the coordinates of all polygons in a cell, as well as origins and offset vectors of
CellArray
s andCellReference
s. - Determine whether the cell will contain integer coordinates or floating-point coordinates. Currently, you cannot do a whole lot (particularly with regard to paths) if the cell has integer coordinates. However, they do have an inherent advantage because the coordinates are exact, and ultimately the GDS-II file represents shapes with integer coordinates. In the future, we intend to improve support for cells with integer coordinates.
For instance, Cell{typeof(1.0u"nm")}
matches a cell where the database unit is nm
and polygons may have Float64
-based coordinates (the type of 1.0
is Float64
). Note that Cell{typeof(2.0u"nm")}
does not mean the database unit is 2.0nm, because the returned type is the same. If that is intended, instead make a new unit such that one of that new unit is equal to 2nm. You can do this using the @unit
macro in Unitful.
For most cases, if you want to use units, Cell("my_cell_name", nm)
is a good way to construct a cell which will ultimately have all coordinates rounded to the nearest nm
when exported into GDS-II. You can add polygons with whatever length units you want to such a cell, and the coordinates will be converted automatically to nm
. You can change nm
to pm
or fm
or whatever, but this will limit the pattern extent and probably doesn't make sense anyway.
If you don't want units, just construct the cell with a name only: Cell("my_cell_name")
will return a Cell{Float64}
object. In this case too, the ultimate database resolution is 1nm
; until exporting the cell into a GDS-II file, the coordinates are interpreted to be in units of 1μm
. This behavior cannot be changed for cells without units.
Cell API
#
Devices.Cells.Cell
— Method.
Cell(name::AbstractString)
Convenience constructor for Cell{Float64}
.
#
Devices.bounds
— Method.
bounds(cell::Cell{T}) where {T <: Coordinate} bounds(cell0::Cell, cell1::Cell, cell::Cell...)
Returns a Rectangle
bounding box around all objects in a cell or cells. Returns a rectangle with zero width and height if the cell or cells are empty.
#
Devices.center
— Method.
center(cell::Cell)
Convenience method, equivalent to center(bounds(cell))
. Returns the center of the bounding box of the cell.
#
Devices.Cells.name
— Method.
name(x::Cell)
Returns the name of the cell.
#
Devices.Cells.dbscale
— Method.
dbscale(c::Cell{T}) where {T}
Give the database scale for a cell. The database scale is the smallest increment of length that will be represented in the output CAD file.
For Cell{T<:Length}
, the database scale is T(1)
. For floating-point lengths, this means that anything after the decimal point will be rounded off. For this reason, Cell{typeof(1.0nm)} is probably the most convenient type to work with.
The database scale of a Cell{T<:Real}
is assumed to be 1nm
(1.0nm
if T <: AbstractFloat
) because insufficient information is provided to know otherwise.
#
Devices.Cells.dbscale
— Method.
dbscale(cell::Cell...)
Choose an appropriate database scale for a GDSII file given Cell
s of different types. The smallest database scale of all cells considered is returned.
Referenced and arrayed cells
Cells can be arrayed or referenced within other cells for efficiency or to reduce display complexity.
#
Devices.Cells.CellArray
— Type.
mutable struct CellArray{S,T} <: CellRef{S,T} cell::T origin::Point{S} deltacol::Point{S} deltarow::Point{S} col::Int row::Int xrefl::Bool mag::Float64 rot::Float64 end
Array of cell
starting at origin
with row
rows and col
columns, spanned by vectors deltacol
and deltarow
. Optional x-reflection xrefl
, magnification factor mag
, and rotation angle rot
for the array as a whole. If an angle is given without units it is assumed to be in radians.
The type variable T
is to avoid circular definitions with Cell
.
#
Devices.Cells.CellArray
— Method.
CellArray(x::Cell{S}; kwargs...) where {S <: Coordinate} CellArray(x::Cell{S}, origin::Point{T}; kwargs...) where {S <: Coordinate, T <: Coordinate} CellArray(x, origin::Point{T}; kwargs...) where {T <: Coordinate}
Construct a CellArray{float(T),typeof(x)}
object.
Keyword arguments specify the column vector, row vector, number of columns, number of rows, x-reflection, magnification factor, and rotation.
Synonyms are accepted for these keywords:
- Column vector
dc::Point{T}
::deltacol
,:dcol
,:dc
,:vcol
,:colv
,:colvec
,:colvector
,:columnv
,:columnvec
,:columnvector
- Row vector:
:deltarow
,:drow
,:dr
,:vrow
,:rv
,:rowvec
,:rowvector
- Number of columns:
:nc
,:numcols
,:numcol
,:ncols
,:ncol
- Number of rows:
:nr
,:numrows
,:numrow
,:nrows
,:nrow
- X-reflection:
:xrefl
,:xreflection
,:refl
,:reflect
,:xreflect
,:xmirror
,:mirror
- Magnification:
:mag
,:magnification
,:magnify
,:zoom
,:scale
- Rotation:
:rot
,:rotation
,:rotate
,:angle
#
Devices.Cells.CellArray
— Method.
CellArray(x, c::AbstractRange, r::AbstractRange; kwargs...)
Construct a CellArray
based on ranges (probably LinSpace
or FloatRange
). c
specifies column coordinates and r
for the rows. Pairs from c
and r
specify the origins of the repeated cells. The extrema of the ranges therefore do not specify the extrema of the resulting CellArray
's bounding box; some care is required.
Keyword arguments specify x-reflection, magnification factor, and rotation, with synonyms allowed:
- X-reflection:
:xrefl
,:xreflection
,:refl
,:reflect
,:xreflect
,:xmirror
,:mirror
- Magnification:
:mag
,:magnification
,:magnify
,:zoom
,:scale
- Rotation:
:rot
,:rotation
,:rotate
,:angle
#
Devices.Cells.CellReference
— Type.
mutable struct CellReference{S,T} <: CellRef{S,T} cell::T origin::Point{S} xrefl::Bool mag::Float64 rot::Float64 end
Reference to a cell
positioned at origin
, with optional x-reflection xrefl
, magnification factor mag
, and rotation angle rot
. If an angle is given without units it is assumed to be in radians.
The type variable T
is to avoid circular definitions with Cell
.
#
Devices.Cells.CellReference
— Method.
CellReference(x::Cell{S}; kwargs...) where {S <: Coordinate} CellReference(x::Cell{S}, origin::Point{T}; kwargs...) where {S <: Coordinate, T <: Coordinate} CellReference(x, origin::Point{T}; kwargs...) where {T <: Coordinate}
Convenience constructor for CellReference{float(T), typeof(x)}
.
Keyword arguments can specify x-reflection, magnification, or rotation. Synonyms are accepted, in case you forget the "correct keyword"...
- X-reflection:
:xrefl
,:xreflection
,:refl
,:reflect
,:xreflect
,:xmirror
,:mirror
- Magnification:
:mag
,:magnification
,:magnify
,:zoom
,:scale
- Rotation:
:rot
,:rotation
,:rotate
,:angle
#
Devices.bounds
— Method.
bounds(ref::CellArray)
Returns a Rectangle
bounding box around all objects in ref
. The bounding box respects reflection, rotation, and magnification specified by ref
.
Please do rewrite this method when feeling motivated... it is very inefficient.
#
Devices.bounds
— Method.
bounds(ref::CellReference)
Returns a Rectangle
bounding box around all objects in ref
. The bounding box respects reflection, rotation, and magnification specified by ref
.
#
Base.copy
— Method.
copy(x::CellReference)
Creates a shallow copy of x
(does not copy the referenced cell).
#
Base.copy
— Method.
copy(x::CellArray)
Creates a shallow copy of x
(does not copy the arrayed cell).
#
Devices.Cells.name
— Method.
name(x::CellReference)
Returns the name of the referenced cell.
#
Devices.Cells.name
— Method.
name(x::CellArray)
Returns the name of the arrayed cell.
#
Devices.Cells.uniquename
— Function.
uniquename(str)
Given string input str
, generate a unique name that bears some resemblance to str
. Useful if programmatically making Cells and all of them will eventually be saved into a GDS-II file. The uniqueness is expected on a per-Julia session basis, so if you load an existing GDS-II file and try to save unique cells on top of that you may get an unlucky clash.
Resolving references
Sometimes it can be helpful to go between coordinate systems of cells and the cells they reference. This package provides methods to generate affine transforms to do this as easily as possible.
#
CoordinateTransformations.transform
— Method.
transform(c::Cell, d::CellRef)
Given a Cell c
containing CellReference
or CellArray
d
in its tree of references, this function returns a CoordinateTransformations.AffineMap
object that lets you translate from the coordinate system of d
to the coordinate system of c
.
If the same exact CellReference
or CellArray
(as in ===
, same address in memory) is included multiple times in the tree of references, then the resulting transform will be based on the first time it is encountered. The tree is traversed one level at a time to find the reference (optimized for shallow references).
Example: You want to translate (2.0,3.0) in the coordinate system of the referenced cell d
to the coordinate system of c
:
julia> trans = transform(c,d) julia> trans(Point(2.0,3.0))
In some cases it may be desirable to resolve cell references or arrays into their corresponding polygons. This operation is called "flattening."
#
Devices.Cells.flatten!
— Function.
flatten!(c::Cell, depth::Integer=-1)
Cell references and arrays up to a hierarchical depth
are recursively flattened into polygons and added to cell c
. The references and arrays that were flattened are then discarded. Deeper cell references and arrays are brought upwards and are not discarded. This function has no effect for depth == 0
, and unlimited depth by default.
#
Devices.Cells.flatten
— Function.
flatten(c::Cell; depth::Integer=-1, name=uniquename("flatten"))
Cell references and arrays in c
up to a hierarchical depth
are recursively flattened into polygons and added to a new Cell
with name name
. The references and arrays that were flattened are then discarded. Deeper cell references and arrays are brought upwards and are not discarded. This function has no effect for depth == 0
, and unlimited depth by default. The cell c
remains unmodified.
flatten(c::CellReference; depth::Integer=-1, name=uniquename("flatten"))
Cell reference c
is recursively flattened into polygons up to a hierarchical depth
and added to a new Cell
with name name
. The references and arrays that were flattened are then discarded. Deeper cell references and arrays are brought upwards and are not discarded. The cell reference c
remains unmodified. The user should not pass depth=0
as that will flatten with unlimited depth.
flatten(c::CellArray; depth::Integer=-1, name=uniquename("flatten"))
Cell array c
is recursively flattened into polygons up to a hierarchical depth
and added to a new Cell
with name name
. The references and arrays that were flattened are then discarded. Deeper cell references and arrays are brought upwards and are not discarded. The cell reference c
remains unmodified. The user should not pass depth=0
as that will flatten with unlimited depth.
Miscellaneous
When saving cells to disk, keep in mind that cells should have unique names. We don't have an automatic renaming scheme implemented to avoid clashes. To help with this, we provide a function uniquename
to generate unique names based on human-readable prefixes.
When saving cells to disk, there will be a tree of interdependencies and logically one would prefer to write the leaf nodes of the tree before any dependent cells. These functions are used to traverse the tree and then find the optimal ordering.
#
Devices.Cells.traverse!
— Function.
traverse!(a::AbstractArray, c::Cell, level=1)
Given a cell, recursively traverse its references for other cells and add to array a
some tuples: (level, c)
. level
corresponds to how deep the cell was found, and c
is the found cell.
#
Devices.Cells.order!
— Function.
order!(a::AbstractArray)
Given an array of tuples like that coming out of traverse!
, we sort by the level
, strip the level out, and then retain unique entries. The aim of this function is to determine an optimal writing order when saving pattern data (although the GDS-II spec does not require cells to be in a particular order, there may be performance ramifications).
For performance reasons, this function modifies a
but what you want is the returned result array.