Index
+ +A
++ |
|
+
B
++ | + |
C
+D
++ | + |
E
++ |
|
+
F
++ | + |
G
++ | + |
H
++ |
I
++ |
J
++ |
L
++ | + |
M
++ |
|
+
N
+
|
+ + |
O
++ | + |
P
++ | + |
Q
+
|
+ + |
R
+S
+T
++ | + |
U
++ |
V
++ |
|
+
W
++ | + |
X
++ | + |
Y
++ |
' + + '' + + _("Hide Search Matches") + + "
" + ) + ); + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords: () => { + document + .querySelectorAll("#searchbox .highlight-link") + .forEach((el) => el.remove()); + document + .querySelectorAll("span.highlighted") + .forEach((el) => el.classList.remove("highlighted")); + localStorage.removeItem("sphinx_highlight_terms") + }, + + initEscapeListener: () => { + // only install a listener if it is really needed + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return; + if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) { + SphinxHighlight.hideSearchWords(); + event.preventDefault(); + } + }); + }, +}; + +_ready(() => { + /* Do not call highlightSearchWords() when we are on the search page. + * It will highlight words from the *previous* search query. + */ + if (typeof Search === "undefined") SphinxHighlight.highlightSearchWords(); + SphinxHighlight.initEscapeListener(); +}); diff --git a/_static/tabs.css b/_static/tabs.css new file mode 100644 index 000000000..13042e567 --- /dev/null +++ b/_static/tabs.css @@ -0,0 +1,110 @@ +/* body[data-theme] { */ +:root { + --tabs--label-text: #4b5563; + --tabs--label-text--hover: #4b5563; + --tabs--label-text--active: #0ea5e9; + --tabs--label-text--active--hover: #0ea5e9; + --tabs--label-background: transparent; + --tabs--label-background--hover: transparent; + --tabs--label-background--active: transparent; + --tabs--label-background--active--hover: transparent; + --tabs--label-border: transparent; + --tabs--label-border--hover: #d1d5db; + --tabs--label-border--active: #0ea5e9; + --tabs--label-border--active--hover: #0ea5e9; + --tabs--padding-x: 1.25em; + --tabs--margin-x: 0; + --tabs--border: #e6e6e6; +} + +/* Hide radio buttons */ +.tab-set > input { + position: absolute; + opacity: 0; +} + +/* Tab set container */ +.tab-set { + border-radius: 2px; + display: flex; + flex-wrap: wrap; + margin: 0.75em 0; + position: relative; +} + +/* Tab label */ +.tab-set > label { + z-index: 1; + + width: auto; + border-bottom: 2px solid var(--tabs--label-border); + padding: 1em var(--tabs--padding-x) 0.5em; + margin-left: var(--tabs--margin-x); + + color: var(--tabs--label-text); + background: var(--tabs--label-background); + + transition: color 250ms; + + cursor: pointer; + + font-size: 0.875em; + font-weight: 700; +} +.tab-set > label:nth-child(2) { + margin-left: 0; +} + +/* Hovered label */ +.tab-set > label:hover { + color: var(--tabs--label-text--hover); + background: var(--tabs--label-background--hover); + border-color: var(--tabs--label-border--hover); +} + +/* Active tab label */ +.tab-set > input:checked + label { + color: var(--tabs--label-text--active); + background: var(--tabs--label-background--active); + border-color: var(--tabs--label-border--active); +} +.tab-set > input:checked + label:hover { + color: var(--tabs--label-text--active--hover); + background: var(--tabs--label-background--active--hover); + border-color: var(--tabs--label-border--active--hover); +} + +/* Tab content */ +.tab-content { + order: 99; + display: none; + width: 100%; + box-shadow: 0 -0.0625rem var(--tabs--border); +} + +/* Show content, when input is checked. */ +.tab-set > input:checked + label + .tab-content { + display: block; +} +.tab-content > p:first-child { + margin-top: 0.75rem; +} +/* Remove the top border on first code block */ +.tab-content > [class^="highlight-"]:first-child .highlight { + border-top: none; + border-top-left-radius: 0; + border-top-right-radius: 0; +} + +/* Remove margins on children */ +.tab-content > *:first-child { + margin-top: 0; +} +.tab-content > *:last-child { + margin-bottom: 0; +} + +/* Remove margins on nested tabs */ +.tab-content > .tab-set { + margin: 0; +} diff --git a/_static/tabs.js b/_static/tabs.js new file mode 100644 index 000000000..58d2cdddd --- /dev/null +++ b/_static/tabs.js @@ -0,0 +1,30 @@ +var labels_by_text = {}; + +function ready() { + var li = document.getElementsByClassName("tab-label"); + const urlParams = new URLSearchParams(window.location.search); + const tabs = urlParams.getAll("tabs"); + + for (const label of li) { + label.onclick = onLabelClick; + const text = label.textContent; + if (!labels_by_text[text]) { + labels_by_text[text] = []; + } + labels_by_text[text].push(label); + } + + for (const tab of tabs) { + for (label of labels_by_text[tab]) { + label.previousSibling.checked = true; + } + } +} + +function onLabelClick() { + // Activate other labels with the same text. + for (label of labels_by_text[this.textContent]) { + label.previousSibling.checked = true; + } +} +document.addEventListener("DOMContentLoaded", ready, false); diff --git a/genindex.html b/genindex.html new file mode 100644 index 000000000..22e80a75f --- /dev/null +++ b/genindex.html @@ -0,0 +1,966 @@ + + + + + ++ |
|
+
+ | + |
+ | + |
+ |
|
+
+ | + |
+ | + |
+ |
+ |
+ |
+ | + |
+ |
|
+
|
+ + |
+ | + |
+ | + |
|
+ + |
+ | + |
+ |
+ |
|
+
+ | + |
+ | + |
+ |
Curve object for construction of complex polygons.
+The methods are loosely based on the definition of SVG paths.
+xy – Curve starting point. It can be a sequence of 2 numbers or a +single complex value.
tolerance – Tolerance used for calculating the polygonal +approximation of the curve.
Examples
+>>> curve = gdstk.Curve((3, 4), tolerance=1e-3)
+>>> curve.segment((1, 1), True)
+>>> curve.turn(1, -numpy.pi / 2)
+>>> curve.segment((1, -1), True)
+>>> polygon = gdstk.Polygon(curve.points())
+
Methods
+
|
+Append an elliptical arc to this curve. |
+
|
+Append a Bézier curve to this curve. |
+
|
+Parse commands to append sections to this curve. |
+
|
+Append cubic Bézier curves to this curve. |
+
|
+Append smooth cubic Bézier curves to this curve. |
+
|
+Append horizontal segments to this curve. |
+
|
+Append a smooth interpolating curve through the given points. |
+
|
+Append a parametric curve to this curve. |
+
|
+Return the polygonal approximation of this curve. |
+
|
+Append quadratic Bézier curves to this curve. |
+
|
+Append smooth quadratic Bézier curves to this curve. |
+
|
+Append straight segments to this curve. |
+
|
+Append a circular turn to this curve. |
+
|
+Append vertical segments to this curve. |
+
Attributes
++ | Tolerance used for calculating the polygonal approximation of the curve. |
+
Append an elliptical arc to this curve.
+radius (number or sequence[2]) – Circular arc radius or elliptical +arc radii.
initial_angle – Starting angle (in radians).
final_angle – Ending angle (in radians).
rotation – Arc rotation.
Examples
+>>> curve = gdstk.Curve((-0.6, 0), tolerance=1e-3)
+>>> curve.segment((1, 0), True)
+>>> curve.arc(1, 0, numpy.pi / 2)
+>>> polygon_1 = gdstk.Polygon(curve.points())
+
>>> curve = gdstk.Curve((0.6, 0), tolerance=1e-3)
+>>> curve.segment((1, 0), True)
+>>> curve.arc((2 ** -0.5, 0.4), -numpy.pi / 4, 3 * numpy.pi / 4,
+... -numpy.pi / 4)
+>>> polygon_2 = gdstk.Polygon(curve.points())
+
Append a Bézier curve to this curve.
+xy (sequence of points) – Curve control points.
relative – If True
, coordinates are relative to the last point.
Examples
+>>> points = [(4, 1), (4, 3), (0, 5), (-4, 3), (-4, -2), (0, -4),
+... (0, 0)]
+>>> curve = gdstk.Curve((0, 0))
+>>> curve.segment(points)
+>>> control_poly = gdstk.Polygon(curve.points(), datatype=1)
+>>> curve = gdstk.Curve((0, 0), tolerance=1e-3)
+>>> curve.bezier(points)
+>>> polygon = gdstk.Polygon(curve.points())
+
Parse commands to append sections to this curve.
+Commands are single characters followed by a pre-defined number of +numerical arguments, according to the table below:
+Command |
+Primitive |
+Arguments |
+
---|---|---|
L/l |
+Line segment |
+x, y |
+
H/h |
+Horizontal segment |
+x |
+
V/v |
+Vertical segment |
+y |
+
C/c |
+Cubic Bézier |
+x0, y0, x1, y1, x2, y2 |
+
S/s |
+Smooth cubic Bézier |
+x0, y0, x1, y1 |
+
Q/q |
+Quadratic Bézier |
+x0, y0, x1, y1 |
+
T/t |
+Smooth quadratic Bézier |
+x, y |
+
a |
+Circular turn |
+rad, ang |
+
A |
+Circular arc |
+rad, ang0, ang1 |
+
E |
+Elliptical arc |
+rad0, rad1, ang0, ang1, rot |
+
Uppercase commands assume that coordinates are absolute, whereas the +lowercase versions assume they are relative to the previous endpoint.
+Notes
+The meaning and order of the arguments of all commands are identical +to the corresponding method.
+Examples
+>>> curve = gdstk.Curve((0, 0), tolerance=1e-3)
+>>> curve.commands("l", 1, 1, "a", 1, -numpy.pi / 2, "l", 1, -1,
+... "S", 1, -2, 0, -2)
+>>> polygon = gdstk.Polygon(curve.points())
+
Append cubic Bézier curves to this curve.
+xy (sequence of points) – Each set of 3 consecutive points is +interpreted as 2 control points and an end point.
relative – If True
, coordinates are relative to the last point.
Examples
+>>> curve = gdstk.Curve((0, 0), tolerance=1e-3)
+>>> curve.cubic([(1, -2), (2, -2), (3, 0)])
+>>> curve.cubic([(2.7, 1), (1.8, 1), (1.5, 0),
+... (1.3, -0.2), (0.3, -0.2), (0, 0)])
+>>> polygon = gdstk.Polygon(curve.points())
+
Append smooth cubic Bézier curves to this curve.
+The first control point is assumed to be the reflection of the last +control point of this curve with respect to its end point.
+xy (sequence of points) – Each set of 2 consecutive points is +interpreted as the second control point and an end point.
relative – If True
, coordinates are relative to the last point.
Examples
+>>> curve = gdstk.Curve((0, 0), tolerance=1e-3)
+>>> curve.cubic([1 + 0j, 2.5 + 1.5j, 2 + 2j])
+>>> curve.cubic_smooth([1j, 0j])
+>>> polygon = gdstk.Polygon(curve.points())
+>>> polygon = gdstk.Polygon(curve.points())
+
Append horizontal segments to this curve.
+x (number or sequence) – End point x coordinates.
relative – If True
, coordinates are relative to the last point.
Append a smooth interpolating curve through the given points.
+Use the Hobby algorithm [1] to calculate a smooth interpolating +curve made of cubic Bezier segments between each pair of points. Angle +and tension parameters can be specified at each point, and the curve can +be open or closed.
+points (sequence[N] of points) – Vertices in the interpolating curve.
angles (None
or sequence[N + 1]) – Tangent angles at each point
+(in radians). Angles defined as None
are automatically
+calculated.
tension_in (number or sequence[N + 1]) – Tension parameter when +arriving at each point. One value per point or a single value used +for all points.
tension_out (number or sequence[N + 1]) – Tension parameter when +leaving each point. One value per point or a single value used for +all points.
initial_curl – Ratio between the mock curvatures at the first point +and at its neighbor. A value of 1 renders the first segment a good +approximation for a circular arc. A value of 0 will better +approximate a straight segment. It has no effect for closed curves +or when an angle is defined for the first point.
final_curl – Ratio between the mock curvatures at the last point and +at its neighbor. It has no effect for closed curves or when an +angle is defined for the first point.
cycle – If True
, calculates control points for a closed curve,
+with an additional segment connecting the first and last points.
relative – If True
, coordinates are relative to the last point.
Examples
+>>> points = [(4, 1), (4, 3), (0, 5), (-4, 3), (-4, -2), (0, -4)]
+>>> curve = gdstk.Curve((0, 0))
+>>> curve.segment(points)
+>>> control_poly_1 = gdstk.Polygon(curve.points(), datatype=1)
+>>> curve = gdstk.Curve((0, 0), tolerance=1e-3)
+>>> curve.interpolation(points, cycle=True)
+>>> polygon_1 = gdstk.Polygon(curve.points())
+
>>> half_pi = numpy.pi / 2
+>>> angles = [half_pi, None, None, None, -half_pi, -half_pi, None]
+>>> curve = gdstk.Curve((4, -9))
+>>> curve.segment(points, relative=True)
+>>> control_poly_2 = gdstk.Polygon(curve.points(), datatype=1)
+>>> curve = gdstk.Curve((4, -9), tolerance=1e-3)
+>>> curve.interpolation(points, angles, cycle=True, relative=True)
+>>> polygon_2 = gdstk.Polygon(curve.points())
+
Append a parametric curve to this curve.
+curve_function (callable) – Function that defines the curve. Must be +a function of one argument (that varies from 0 to 1) that returns +a 2-element sequence or complex with the coordinates of the curve.
relative – If True
, the return values of curve_function
are
+used as offsets from the current path position, i.e., to ensure a
+continuous path, curve_function(0)
must be (0, 0). Otherwise,
+they are used as absolute coordinates.
Examples
+>>> pi = numpy.pi
+>>> def top(u):
+... x = 4 * u
+... y = 1 - numpy.cos(4 * pi * u)
+... return (x, y)
+...
+>>> curve = gdstk.Curve((-2, 0), tolerance=1e-3)
+>>> curve.parametric(top)
+>>> curve.parametric(
+... lambda u: (4 - 2 * u ** 0.5) * numpy.exp(-1.5j * pi * u) - 4
+... )
+>>> polygon = gdstk.Polygon(curve.points())
+
Return the polygonal approximation of this curve.
+Examples
+>>> curve = gdstk.Curve((0, 0))
+>>> curve.segment([(1, 0), (0, 1)])
+>>> curve.points()
+array([[0., 0.],
+ [1., 0.],
+ [0., 1.]])
+
Append quadratic Bézier curves to this curve.
+xy (sequence of points) – Each set of 2 consecutive points is +interpreted as control point and an end point.
relative – If True
, coordinates are relative to the last point.
See also
+ +Append smooth quadratic Bézier curves to this curve.
+The control point is assumed to be the reflection of the last control +point of this curve with respect to its end point.
+xy (sequence of points) – Curve end points.
relative – If True
, coordinates are relative to the last point.
See also
+ +Append straight segments to this curve.
+xy – Segment end point. It can be a sequence of 2 numbers or a single +complex. Multiple segments can be constructed by using a sequence +of points.
relative – If True
, coordinates are relative to the last point.
Examples
+>>> curve = gdstk.Curve((1, 0))
+>>> curve.segment((0, 1))
+>>> curve.segment([0j, -1+0j])
+>>> curve.segment([(0, -1), (2, -1)], True)
+>>> polygon = gdstk.Polygon(curve.points())
+
Tolerance used for calculating the polygonal approximation of the curve.
+Examples
+>>> curve = gdstk.Curve((-2.5, 0), tolerance=1e-1)
+>>> curve.arc((2, 3), 0, numpy.pi)
+>>> polygon_1 = gdstk.Polygon(curve.points())
+>>> print(polygon_1.size)
+7
+>>> curve = gdstk.Curve((2.5, 0), tolerance=1e-3)
+>>> curve.arc((2, 3), 0, numpy.pi)
+>>> polygon_2 = gdstk.Polygon(curve.points())
+>>> print(polygon_2.size)
+62
+
Append a circular turn to this curve.
+radius – Circular arc radius.
angle – Turning angle. Positive values turn counter clockwise and +negative values, clockwise.
Append vertical segments to this curve.
+y (number or sequence) – End point y coordinates.
relative – If True
, coordinates are relative to the last point.
Flexible path creation.
+FlexPath can be used to create single and multiple parallel paths with +controlled widths and offsets. Joins and end caps can be customized +freely and, if desired, bends can be automatically created to join path +segments continuously.
+Adjacent sections are expected to be joined smoothly when at least one
+of them is curved. If that condition cannot be met,
+gdstk.RobustPath
should be used instead.
points – Single point or sequence of points that make up the path. +Each point can be a pair of coordinates or a complex value.
width (number or sequence) – Width of the paths. If this is a +sequence, its length defines the number of paths created.
offset (number or sequence) – If this is a number, it is treated as +the distance between centers of adjacent paths. If it is a +sequence, each number represents the absolute offset from the +center of the path.
joins – Definition for the joins. One of “natural”, “miter”, “bevel”, +“round”, “smooth”, or a callable. A callable must accept 6 +arguments (end point and direction vector for each edge adjacent +to the join, the center of the join, and the path width) and +return a sequence of points that define the join shape.
ends – Definition for the end caps. One of “flush”, “extended”, +“round”, “smooth”, a 2-tuple, or a callable. A 2-tuple defines the +extension length on both ends of the path. A callable must accept +4 arguments (cap end point and direction for both path sides) and +return a sequence of points that define the cap shape.
bend_radius (number or sequence) – If a positive number, path +segments are joined by a bend shape (defaults to a circular arc) +when possible.
bend_function (callable) – A callable that takes 4 arguments (radius, +initial and final angles, and the bend center) and returns a +sequence of points defining the bend shape.
tolerance – Tolerance used for calculating the polygonal +approximation of the paths.
simple_path – If True
, the paths will be stored as GDSII path
+elements. They require less memory, but do not support “smooth” or
+callable joins and end caps, or width changes along the path.
scale_width – If False
, the path widths are not scaled when
+transforming this path.
layer – layer number assigned to this path.
datatype – data type number assigned to this path.
Notes
+If width
is a number and offset
a sequence, the number of
+parallel paths is defined by the latter.
Arguments joins
, ends
, bend_radius
, bend_function
,
+layer
, and datatype
can also be lists with one definition
+for each path created.
Examples
+>>> path = gdstk.FlexPath(
+... [(0, 5), (0, 0), (5, 0), (15, 10), (15, -5)],
+... [0.8, 0.8, 0.8, 0.8],
+... 1.0,
+... joins=["natural", "bevel", "miter", "round"],
+... ends=["flush", "extended", (0.4, 0.8), "round"],
+... layer=[0, 1, 2, 3],
+... )
+
>>> points = [(0, 8), (0, 0), (8, 0), (18, 13), (18, -8)]
+>>> path_1 = gdstk.FlexPath(points , 1, datatype=1)
+>>> path_2 = gdstk.FlexPath(points , 1, bend_radius=3)
+
>>> def custom_broken_join(p0, v0, p1, v1, center, width):
+... p0 = numpy.array(p0)
+... v0 = numpy.array(v0)
+... p1 = numpy.array(p1)
+... v1 = numpy.array(v1)
+... center = numpy.array(center)
+... # Calculate intersection point p between lines defined by
+... # p0 + u0 * v0 (for all u0) and p1 + u1 * v1 (for all u1)
+... den = v1[1] * v0[0] - v1[0] * v0[1]
+... lim = 1e-12 * ((v0[0] ** 2 + v0[1] ** 2) *
+... (v1[0] ** 2 + v1[1] ** 2))
+... if den ** 2 < lim:
+... # Lines are parallel: use mid-point
+... u0 = u1 = 0
+... p = 0.5 * (p0 + p1)
+... else:
+... dx = p1[0] - p0[0]
+... dy = p1[1] - p0[1]
+... u0 = (v1[1] * dx - v1[0] * dy) / den
+... u1 = (v0[1] * dx - v0[0] * dy) / den
+... p = 0.5 * (p0 + v0 * u0 + p1 + v1 * u1)
+... if u0 <= 0 and u1 >= 0:
+... # Inner corner
+... return [p]
+... # Outer corner
+... return [p0, center, p1]
+>>> def custom_pointy_end(p0, v0, p1, v1):
+... p0 = numpy.array(p0)
+... v0 = numpy.array(v0)
+... p1 = numpy.array(p1)
+... v1 = numpy.array(v1)
+... r = 0.5 * numpy.sqrt(numpy.sum((p0 - p1) ** 2))
+... v0 /= numpy.sqrt(numpy.sum(v0 ** 2))
+... v1 /= numpy.sqrt(numpy.sum(v1 ** 2))
+... return [p0, 0.5 * (p0 + p1) + 0.5 * (v0 - v1) * r, p1]
+>>> path = gdstk.FlexPath(
+... [(0, 5), (0, 0), (5, 0), (15, 10), (15, -5)],
+... 3,
+... joins=custom_broken_join,
+... ends=custom_pointy_end,
+... )
+
Methods
++ | Create new flexpaths based on this object's |
+
|
+Append an elliptical arc to this path. |
+
|
+Append a Bézier curve to this path. |
+
|
+Append sections to this path according to commands. |
+
|
+Create a copy this flexpath. |
+
|
+Append cubic Bézier curves to this path. |
+
|
+Append smooth cubic Bézier curves to this path. |
+
|
+Delete a GDSII property of this element. |
+
|
+Delete the first property of this element matching a name. |
+
|
+Return a GDSII property of this element. |
+
|
+Return the values of the first property of this element matching a name. |
+
|
+Append horizontal segments to this path. |
+
|
+Append a smooth interpolating curve through the given points. |
+
|
+Mirror this path across the line through p1 and p2. |
+
|
+Offsets of all paths at every point. |
+
|
+Append a parametric curve to this path. |
+
+ | Central spines of each parallel path. |
+
|
+Append quadratic Bézier curves to this path. |
+
|
+Append smooth quadratic Bézier curves to this path. |
+
|
+Rotate this path. |
+
|
+Scale this path. |
+
|
+Append straight segments to this path. |
+
|
+Set the bend function for all paths. |
+
|
+Set the bend radius for all paths. |
+
|
+Set the datatypes for all paths. |
+
|
+Set the end types for all paths. |
+
|
+Set a GDSII property for this element. |
+
|
+Set the end types for all paths. |
+
|
+Set the layers for all paths. |
+
|
+Set a property for this element. |
+
|
+Central path spine. |
+
+ | Calculate the polygonal representations of this path. |
+
|
+Translate this path. |
+
|
+Append a circular turn to this path. |
+
|
+Append vertical segments to this path. |
+
|
+Widths of all paths at every point. |
+
Attributes
++ | Bend function for each path. |
+
+ | Bend radius for each path. |
+
+ | FlexPath data type. |
+
+ | End types for each path. |
+
+ | Join types for each path. |
+
+ | FlexPath layer. |
+
+ | Number of paths. |
+
+ | Properties of this element. |
+
+ | Raith electron beam lithography data. |
+
+ | Repetition associated with this element. |
+
+ | Scale width flag. |
+
+ | Simple path flag. |
+
+ | Number of points in the path spine. |
+
+ | Path tolerance. |
+
Create new flexpaths based on this object’s repetition
attribute.
After the repetition is applied, the original attribute is set to
+None
.
Newly created objects.
+Append an elliptical arc to this path.
+radius (number or sequence[2]) – Circular arc radius or elliptical +arc radii.
initial_angle – Starting angle (in radians).
final_angle – Ending angle (in radians).
rotation – Arc rotation.
width (number or sequence) – Width at the end point(s). If this is a +sequence, it must define the width for each path. The width is +linearly tapered from its previous value.
offset (number or sequence) – If this is a number, it is treated as +the distance between centers of adjacent paths. If it is a +sequence, each number represents the absolute offset from the +center of the path. The offsets are linearly tapered from their +previous values.
Examples
+>>> path = gdstk.FlexPath((0, 0), [0.2, 0.3], 0.4, tolerance=1e-3)
+>>> path.vertical(5)
+>>> path.arc(2.5, numpy.pi, 0)
+>>> path.arc(5, -numpy.pi, -numpy.pi / 2)
+
Bend function for each path.
+Notes
+This attribute is read-only.
+See also
+ +Bend radius for each path.
+Notes
+This attribute is read-only.
+See also
+ +Append a Bézier curve to this path.
+xy (sequence of points) – Curve control points.
width (number or sequence) – Width at the end point(s). If this is a +sequence, it must define the width for each path. The width is +linearly tapered from its previous value.
offset (number or sequence) – If this is a number, it is treated as +the distance between centers of adjacent paths. If it is a +sequence, each number represents the absolute offset from the +center of the path. The offsets are linearly tapered from their +previous values.
relative – If True
, coordinates are relative to the last point.
Examples
+>>> path = gdstk.FlexPath((0, 0), 0.2, tolerance=1e-3)
+>>> path.bezier([(4, 1), (4, 3), (0, 5),
+... (-4, 3), (-4, -2), (0, -4)])
+
Append sections to this path according to commands.
+Commands are single characters followed by a pre-defined number of +numerical arguments, according to the table below:
+Command |
+Primitive |
+Arguments |
+
---|---|---|
L/l |
+Line segment |
+x, y |
+
H/h |
+Horizontal segment |
+x |
+
V/v |
+Vertical segment |
+y |
+
C/c |
+Cubic Bézier |
+x0, y0, x1, y1, x2, y2 |
+
S/s |
+Smooth cubic Bézier |
+x0, y0, x1, y1 |
+
Q/q |
+Quadratic Bézier |
+x0, y0, x1, y1 |
+
T/t |
+Smooth quadratic Bézier |
+x, y |
+
a |
+Circular turn |
+rad, ang |
+
A |
+Circular arc |
+rad, ang0, ang1 |
+
E |
+Elliptical arc |
+rad0, rad1, ang0, ang1, rot |
+
Uppercase commands assume that coordinates are absolute, whereas the +lowercase versions assume they are relative to the previous endpoint.
+Notes
+The meaning and order of the arguments of all commands are identical +to the corresponding method.
+Examples
+>>> path = gdstk.FlexPath((0, 0), [0.2, 0.4, 0.2], 0.5,
+... tolerance=1e-3)
+>>> path.commands("l", 3, 4,
+... "A", 2, numpy.arctan2(3, -4), numpy.pi / 2,
+... "h", 0.5,
+... "a", 3, -numpy.pi)
+
Create a copy this flexpath.
+Copy of this flexpath.
+Append cubic Bézier curves to this path.
+xy (sequence of points) – Each set of 3 consecutive points is +interpreted as 2 control points and an end point.
width (number or sequence) – Width at the end point(s). If this is a +sequence, it must define the width for each path. The width is +linearly tapered from its previous value.
offset (number or sequence) – If this is a number, it is treated as +the distance between centers of adjacent paths. If it is a +sequence, each number represents the absolute offset from the +center of the path. The offsets are linearly tapered from their +previous values.
relative – If True
, coordinates are relative to the last point.
Notes
+Changes in width and offset are split equally among curve parts
+defined by xy
.
Examples
+>>> path = gdstk.FlexPath((0, 0), 0.2, tolerance=1e-3)
+>>> path.cubic([(0, 1), (1, 1), (1, 0)])
+>>> path.cubic([(1, -1), (2, -1), (2.5, -0.5),
+... (3, 0), (3, 1), (2, 1)], width=0.5)
+
Append smooth cubic Bézier curves to this path.
+The first control point is assumed to be the reflection of the last +control point of this path with respect to its end point.
+xy (sequence of points) – Each set of 2 consecutive points is +interpreted as the second control points and an end point.
width (number or sequence) – Width at the end point(s). If this is a +sequence, it must define the width for each path. The width is +linearly tapered from its previous value.
offset (number or sequence) – If this is a number, it is treated as +the distance between centers of adjacent paths. If it is a +sequence, each number represents the absolute offset from the +center of the path. The offsets are linearly tapered from their +previous values.
relative – If True
, coordinates are relative to the last point.
Notes
+Changes in width and offset are split equally among curve parts
+defined by xy
.
Examples
+>>> path = gdstk.FlexPath((0, 0), 0.2, tolerance=1e-3)
+>>> path.cubic([(0, 1), (1, 1), (1, 0)])
+>>> path.cubic_smooth([(2, -1), (2.5, -0.5), (3, 1), (2, 1)],
+... width=0.5)
+
FlexPath data type.
+Notes
+This attribute is read-only.
+See also
+ +Delete a GDSII property of this element.
+attr (number) – Property number.
+Delete the first property of this element matching a name.
+name (str) – Property name.
+End types for each path.
+Notes
+This attribute is read-only.
+See also
+ +Return a GDSII property of this element.
+attr (number) – Property number.
+Property value. If the property number does not exist,
+None
is returned.
str or None
+Return the values of the first property of this element matching a name.
+name (str) – Property name.
+List of property values. If no property is found,
+None
is returned.
list or None
+Append horizontal segments to this path.
+x (number or sequence) – End point x coordinates.
width (number or sequence) – Width at the end point(s). If this is a +sequence, it must define the width for each path. The width is +linearly tapered from its previous value.
offset (number or sequence) – If this is a number, it is treated as +the distance between centers of adjacent paths. If it is a +sequence, each number represents the absolute offset from the +center of the path. The offsets are linearly tapered from their +previous values.
relative – If True
, coordinates are relative to the last point.
Notes
+If x
is a sequence, changes in width and offset are split
+equally among segments.
Examples
+>>> path = gdstk.FlexPath([(0, 0)], 0.2)
+>>> path.horizontal(2, width=0.4, relative=True)
+>>> path.horizontal(2, offset=[0.4], relative=True)
+>>> path.horizontal(2, relative=True)
+>>> print(path.spine())
+... [[0. 0.]
+... [2. 0.]
+... [4. 0.]
+... [6. 0.]]
+
See also
+ +Append a smooth interpolating curve through the given points.
+Use the Hobby algorithm [1] to calculate a smooth interpolating +path made of cubic Bézier segments between each pair of points. Angle +and tension parameters can be specified at each point, and the path can +be open or closed.
+points (sequence[N] of points) – Vertices in the interpolating path.
angles (None
or sequence[N + 1]) – Tangent angles at each point
+(in radians). Angles defined as None
are automatically
+calculated.
tension_in (number or sequence[N + 1]) – Tension parameter when +arriving at each point. One value per point or a single value used +for all points.
tension_out (number or sequence[N + 1]) – Tension parameter when +leaving each point. One value per point or a single value used for +all points.
initial_curl – Ratio between the mock curvatures at the first point +and at its neighbor. A value of 1 renders the first segment a good +approximation for a circular arc. A value of 0 will better +approximate a straight segment. It has no effect for closed paths +or when an angle is defined for the first point.
final_curl – Ratio between the mock curvatures at the last point and +at its neighbor. It has no effect for closed paths or when an +angle is defined for the first point.
cycle – If True
, calculates control points for a closed path,
+with an additional segment connecting the first and last points.
width (number or sequence) – Width at the end point(s). If this is a +sequence, it must define the width for each path. The width is +linearly tapered from its previous value.
offset (number or sequence) – If this is a number, it is treated as +the distance between centers of adjacent paths. If it is a +sequence, each number represents the absolute offset from the +center of the path. The offsets are linearly tapered from their +previous values.
relative – If True
, coordinates are relative to the last point.
Examples
+>>> half_pi = numpy.pi / 2
+>>> points = [(4, 1), (4, 3), (0, 5), (-4, 3), (-4, -2), (0, -4)]
+>>> angles = [half_pi, None, None, None, -half_pi, -half_pi, None]
+>>> path_1 = gdstk.FlexPath((0, 0), 0.2, tolerance=1e-3)
+>>> path_1.interpolation(points, cycle=True)
+>>> path_2 = gdstk.FlexPath((6, -8), 0.2, tolerance=1e-3)
+>>> path_2.interpolation(points, angles, cycle=True, relative=True)
+
Hobby, J.D. “Smooth, easy to compute interpolating splines.” +Discrete Comput Geom 1, 123–140 (1986). DOI: 10.1007/BF02187690.
+Join types for each path.
+Notes
+This attribute is read-only.
+See also
+ +FlexPath layer.
+Notes
+This attribute is read-only.
+See also
+ +Mirror this path across the line through p1 and p2.
+p1 (coordinate pair or complex) – First point in the mirror line.
p2 (coordinate pair or complex) – Second point in the mirror line.
Number of paths.
+Notes
+This attribute is read-only.
+Offsets of all paths at every point.
+The offsets of each path at each point.
+Append a parametric curve to this path.
+path_function (callable) – Function that defines the path. Must be a +function of one argument (that varies from 0 to 1) that returns a +2-element sequence or complex with the coordinates of the path.
width (number or sequence) – Width at the end point(s). If this is a +sequence, it must define the width for each path. The width is +linearly tapered from its previous value.
offset (number or sequence) – If this is a number, it is treated as +the distance between centers of adjacent paths. If it is a +sequence, each number represents the absolute offset from the +center of the path. The offsets are linearly tapered from their +previous values.
relative – If True
, the return values of path_function
are
+used as offsets from the current path position, i.e., to ensure a
+continuous path, path_function(0)
must be (0, 0). Otherwise,
+they are used as absolute coordinates.
Examples
+>>> def spiral(u):
+... rad = 2 * u ** 0.5
+... ang = 3 * numpy.pi * u
+... return (rad * numpy.cos(ang), rad * numpy.sin(ang))
+>>> path = gdstk.FlexPath((0, 0), 0.2, tolerance=1e-3)
+>>> path.parametric(spiral)
+
Central spines of each parallel path.
+Copy of the points that make up each parallel path.
+Properties of this element.
+Properties are represented as a list of lists, each containing the +property name followed by its values.
+Append quadratic Bézier curves to this path.
+xy (sequence of points) – Each set of 2 consecutive points is +interpreted as control point and an end point.
width (number or sequence) – Width at the end point(s). If this is a +sequence, it must define the width for each path. The width is +linearly tapered from its previous value.
offset (number or sequence) – If this is a number, it is treated as +the distance between centers of adjacent paths. If it is a +sequence, each number represents the absolute offset from the +center of the path. The offsets are linearly tapered from their +previous values.
relative – If True
, coordinates are relative to the last point.
Notes
+Changes in width and offset are split equally among curve parts
+defined by xy
.
See also
+ +Append smooth quadratic Bézier curves to this path.
+The control point is assumed to be the reflection of the last control +point of this curve with respect to its end point.
+xy (sequence of points) – Curve end points.
width (number or sequence) – Width at the end point(s). If this is a +sequence, it must define the width for each path. The width is +linearly tapered from its previous value.
offset (number or sequence) – If this is a number, it is treated as +the distance between centers of adjacent paths. If it is a +sequence, each number represents the absolute offset from the +center of the path. The offsets are linearly tapered from their +previous values.
relative – If True
, coordinates are relative to the last point.
Notes
+Changes in width and offset are split equally among curve parts
+defined by xy
.
See also
+ +Raith electron beam lithography data.
+Repetition associated with this element.
+Rotate this path.
+angle – Rotation angle (in radians).
center (coordinate pair or complex) – Center of the transformation.
Scale this path.
+s – Scaling factor.
center (coordinate pair or complex) – Center of the transformation.
Scale width flag.
+Append straight segments to this path.
+xy – Segment end point or sequence of end points. Each point can be a +pair of coordinates or a complex value.
width (number or sequence) – Width at the end point(s). If this is a +sequence, it must define the width for each path. The width is +linearly tapered from its previous value.
offset (number or sequence) – If this is a number, it is treated as +the distance between centers of adjacent paths. If it is a +sequence, each number represents the absolute offset from the +center of the path. The offsets are linearly tapered from their +previous values.
relative – If True
, coordinates are relative to the last point.
Notes
+If xy
is a sequence, changes in width and offset are split
+equally among segments.
Examples
+>>> points = [(1, 0), (1, 1), (-1, 1), (-1, -1), (1, -1)]
+>>> path_1 = gdstk.FlexPath((0, 0), 0.2)
+>>> path_1.segment(points, 0.6)
+>>> path_2 = gdstk.FlexPath((3, 0), [0.1, 0.1], 0.2)
+>>> path_2.segment(points, offset=0.6, relative=True)
+
Set the bend function for all paths.
+functions – Sequence of callable or None
.
See also
+ +Set the bend radius for all paths.
+radii – Sequence of floats.
+See also
+ +Set the datatypes for all paths.
+datatypes – data type numbers for all paths.
+Set the end types for all paths.
+ends – Sequence of “flush”, “extended”, “round”, “smooth”, a 2-tuple, +or a callable.
+See also
+ +Set a GDSII property for this element.
+GDSII properties are stored under the special name “S_GDS_PROPERTY”, as +defined by the OASIS specification.
+attr (number) – Property number.
value (str) – Property value.
Set the end types for all paths.
+joins – Sequence of “natural”, “miter”, “bevel”, “round”, “smooth”, +or a callable.
+See also
+ +Set the layers for all paths.
+layers – layer numbers for all paths.
+Set a property for this element.
+The property name does not have to be unique. Multiple properties can +have the same name.
+name (str) – Property name.
value (str, bytes, number, or sequence of those) – Values associated +with the property.
Notes
+These properties can be used to associate custom metadata with an +element, but general properties are not supported by GDSII files, +only OASIS. Use the specific methods to access GDSII properties.
+Simple path flag.
+Number of points in the path spine.
+Notes
+This attribute is read-only.
+Central path spine.
+Copy of the points that make up the path at zero offset.
+Calculate the polygonal representations of this path.
+The polygonal contours defined by this path.
+Path tolerance.
+Translate this path.
+dx – Translation in the x coordinate or translation vector.
dy – Translation in the y coordinate.
Append a circular turn to this path.
+radius – Circular arc radius.
angle – Turning angle. Positive values turn counter clockwise and +negative values, clockwise.
width (number or sequence) – Width at the end point(s). If this is a +sequence, it must define the width for each path. The width is +linearly tapered from its previous value.
offset (number or sequence) – If this is a number, it is treated as +the distance between centers of adjacent paths. If it is a +sequence, each number represents the absolute offset from the +center of the path. The offsets are linearly tapered from their +previous values.
Append vertical segments to this path.
+y (number or sequence) – End point y coordinates.
width (number or sequence) – Width at the end point(s). If this is a +sequence, it must define the width for each path. The width is +linearly tapered from its previous value.
offset (number or sequence) – If this is a number, it is treated as +the distance between centers of adjacent paths. If it is a +sequence, each number represents the absolute offset from the +center of the path. The offsets are linearly tapered from their +previous values.
relative – If True
, coordinates are relative to the last point.
Notes
+If y
is a sequence, changes in width and offset are split
+equally among segments.
See also
+ +Widths of all paths at every point.
+The widths of each path at each point.
+Polygonal geometric object.
+points (sequence) – Vertices of the polygon. Each element can be a +pair of coordinates or a complex value.
layer – layer number assigned to this polygon.
datatype – data type number assigned to this polygon.
Examples
+>>> polygon_1 = gdstk.Polygon([(0, 0), (1, 0), 1 + 1.5j, 1j])
+>>> polygon_2 = gdstk.Polygon([0j, (-1, 1), (-1, 0)], 2, 3)
+
Methods
++ | Create new polygons based on this object's |
+
|
+Polygon area. |
+
+ | Calculate the polygon bounding box. |
+
|
+Check whether points are inside this polygon. |
+
|
+Check whether all points are inside this polygon. |
+
|
+Check whether any of the points are inside this polygon. |
+
|
+Create a copy this polygon. |
+
|
+Delete a GDSII property of this element. |
+
|
+Delete the first property of this element matching a name. |
+
|
+Fillet the corners of this polygon. |
+
|
+Fracture this polygon into a list of polygons. |
+
|
+Return a GDSII property of this element. |
+
|
+Return the values of the first property of this element matching a name. |
+
|
+Mirror this polygon across the line through |
+
+ | Polygon perimeter. |
+
|
+Rotate this polygon. |
+
|
+Scale this polygon. |
+
|
+Set a GDSII property for this element. |
+
|
+Set a property for this element. |
+
|
+Transform the vertices of this polygon. |
+
|
+Translate this polygon. |
+
Attributes
++ | Polygon data type. |
+
+ | Polygon layer. |
+
+ | Vertices of the polygon. |
+
+ | Properties of this element. |
+
+ | Repetition associated with this element. |
+
+ | Number of vertices in this polygon. |
+
Create new polygons based on this object’s repetition
attribute.
After the repetition is applied, the original attribute is set to
+None
.
Newly created objects.
+Polygon area.
+Area of the polygon.
+Calculate the polygon bounding box.
+The lower-left and upper-right corners of the bounding box of the
+polygon: ((min_x, min_y), (max_x, max_y))
.
Examples
+>>> polygon = gdstk.Polygon([(0, 1), (1, 2), (3, -1)])
+>>> bbox = polygon.bounding_box()
+>>> print(bbox)
+((0.0, -1.0), (3.0, 2.0))
+>>> polygon_bb = gdstk.rectangle(*bbox, datatype=1)
+
Check whether points are inside this polygon.
+Points on the polygon edges or coinciding with any of its vertices are +considered inside.
+points (point or sequence of points) – Points to check. Arguments +can be a pair of coordinates, a complex number, or a sequence of +those.
+If the argument is a single point, returns a boolean. In the case of +a sequence, a tuple of booleans (one for each point) is returned.
+Check whether all points are inside this polygon.
+Points on the polygon edges or coinciding with any of its vertices are +considered inside.
+points – Points to check. Each point can be a pair of coordinates or +a complex number.
+True if all points are inside the polygon, False otherwise.
+Check whether any of the points are inside this polygon.
+Points on the polygon edges or coinciding with any of its vertices are +considered inside.
+points – Points to check. Each point can be a pair of coordinates or +a complex number.
+True if any of the points are inside the polygon, False +otherwise.
+Create a copy this polygon.
+Copy of this polygon.
+Polygon data type.
+Delete a GDSII property of this element.
+attr (number) – Property number.
+Delete the first property of this element matching a name.
+name (str) – Property name.
+Fillet the corners of this polygon.
+radius (number or sequence) – Fillet radius. A sequence of values can +be used to define the fillet radius for each vertex.
tolerance – Tolerance used for calculating the polygonal +approximation of the filleted corners.
Notes
+The actual fillet radius for a given corner is the specified value +or half the length of the shortest segment adjacent to that corner, +whichever is smaller.
+Examples
+>>> points = [(0, 0), (1.2, 0), (1.2, 0.3), (1, 0.3), (1.5, 1),
+... (0, 1.5)]
+>>> polygon_1 = gdstk.Polygon(points, datatype=1)
+>>> polygon_2 = gdstk.Polygon(points).fillet(0.3, tolerance=1e-3)
+
Fracture this polygon into a list of polygons.
+max_points – Maximal number of vertices for each resulting polygon. +Official GDSII documentation requires that all polygons have at +most 199 vertices, but 8190 is usually supported by most software.
precision – Desired vertex precision for fracturing.
List of fractured polygons.
+Notes
+If max_points < 5
the return value is an empty list.
Examples
+>>> polygon = gdstk.racetrack((0, 0), 30, 60, 40, tolerance=1e-3)
+>>> poly_list = polygon.fracture()
+>>> print(len(poly_list))
+10
+>>> print([p.size for p in poly_list])
+[102, 103, 103, 101, 101, 102, 102, 103, 103, 102]
+
Return a GDSII property of this element.
+attr (number) – Property number.
+Property value. If the property number does not exist,
+None
is returned.
str or None
+Return the values of the first property of this element matching a name.
+name (str) – Property name.
+List of property values. If no property is found,
+None
is returned.
list or None
+Polygon layer.
+Mirror this polygon across the line through p1
and p2
.
p1 (coordinate pair or complex) – First point in the mirror line.
p2 (coordinate pair or complex) – Second point in the mirror line.
Polygon perimeter.
+Perimeter of the polygon.
+Vertices of the polygon.
+Notes
+This attribute is read-only.
+Properties of this element.
+Properties are represented as a list of lists, each containing the +property name followed by its values.
+Repetition associated with this element.
+Rotate this polygon.
+angle – Rotation angle (in radians).
center (coordinate pair or complex) – Center of the transformation.
Scale this polygon.
+sx – Scaling in the x direction.
sy – Scaling in the y direction. If set to 0, sx
is used instead.
center (coordinate pair or complex) – Center of the transformation.
Set a GDSII property for this element.
+GDSII properties are stored under the special name “S_GDS_PROPERTY”, as +defined by the OASIS specification.
+attr (number) – Property number.
value (str) – Property value.
Set a property for this element.
+The property name does not have to be unique. Multiple properties can +have the same name.
+name (str) – Property name.
value (str, bytes, number, or sequence of those) – Values associated +with the property.
Notes
+These properties can be used to associate custom metadata with an +element, but general properties are not supported by GDSII files, +only OASIS. Use the specific methods to access GDSII properties.
+Number of vertices in this polygon.
+Notes
+This attribute is read-only.
+Transform the vertices of this polygon.
+The transformation if applied in the order of the arguments in the +method signature, starting with a magnification. If a transformation +matrix is given, it is applied after the other transforms.
+magnification (number) – Magnification factor.
x_reflection (bool) – If True, the vertices are reflected across +the x axis.
rotation (number) – Rotation angle (in radians).
translation (coordinate pair or complex) – Translation vector.
matrix (matrix-like) – Transformation matrix with 2 or 3 rows and 2 +or 3 columns.
Notes
+If the number of rows or columns of the transformation matrix is 3, +homogeneous coordinates are used.
+Translate this polygon.
+dx – Translation in the x coordinate or translation vector.
dy – Translation in the y coordinate.
Repetition object that creates multiple identical elements.
+A repetition can be set in any polygon, path, label or reference to +indicate that the element should be copied to multiple positions. +Repetitions can be of 5 different types, depending on which arguments +are set:
+Rectangular: requires columns, rows and spacing. Creates a +rectangular array of copies.
Regular: requires columns, rows, v1 and v2. Creates an array +of copies along the specified direction vectors.
Explicit: requires offsets. Creates copies at the specified position +offsets. The original element at offset (0, 0) is always present, so +it should not be specified in the offsets array.
X-explicit: requires x-offsets. Creates a linear array of copies +at the given x coordinates. Coordinate 0 is always included, so it +should not be specified.
Y-explicit: requires y-offsets. Creates a linear array of copies +at the given y coordinates. Coordinate 0 is always included, so it +should not be specified.
columns (int) – Number of copies in the x direction (rectangular) or +along v1 (regular).
rows (int) – Number of copies in the y direction (rectangular) or +along v2 (regular).
spacing (coordinate pair or complex) – Step sizes in x and y +directions for rectangular array.
v1 (coordinate pair or complex) – First vector defining the shape of +a regular array.
v2 (coordinate pair or complex) – Second vector defining the shape of +a regular array.
offsets (sequence of points) – Explicit offsets for copies.
x_offsets (sequence of numbers) – Explicit x coordinates for copies.
y_offsets (sequence of numbers) – Explicit y coordinates for copies.
Examples
+>>> ref_array = gdstk.Reference("Some Cell")
+>>> # Create an array of 3 by 2 references:
+>>> ref_array.repetition = gdstk.Repetition(
+... offsets=[(10, 10), (20, 0), (10, -10)]
+... )
+
Notes
+All attributes of Repetition objects are read-only.
+Methods
++ | Calculate all offsets generated by this repetition. |
+
Attributes
++ | Number of columns in a rectangular or regular array. |
+
+ | Offsets for an explicit array. |
+
+ | Number of rows in a rectangular or regular array. |
+
+ | Number of offsets generated by this repetition. |
+
+ | Spacing in a rectangular array. |
+
+ | First vector defining a regular array. |
+
+ | Second vector defining a regular array. |
+
+ | X coordinates for an x-explicit array. |
+
+ | Y coordinates for a y-explicit array. |
+
Number of columns in a rectangular or regular array.
+Calculate all offsets generated by this repetition.
+Offsets generated by this repetition.
+Offsets for an explicit array.
+Number of rows in a rectangular or regular array.
+Number of offsets generated by this repetition.
+Spacing in a rectangular array.
+First vector defining a regular array.
+Second vector defining a regular array.
+X coordinates for an x-explicit array.
+Y coordinates for a y-explicit array.
+Robust path creation.
+RobusPath can be used to create single and multiple parallel paths with +parametrized widths and offsets. End caps can be customized freely. +Intersections between adjacent sections are numerically determined so +that curved sections do not need to be joined smoothly.
+initial_point (coordinate pair or complex) – Starting point of the +path.
width (number or sequence) – Width of the paths. If this is a +sequence, its length defines the number of paths created.
offset (number or sequence) – If this is a number, it is treated as +the distance between centers of adjacent paths. If it is a +sequence, each number represents the absolute offset from the +center of the path.
ends – Definition for the end caps. One of “flush”, “extended”, +“round”, “smooth”, a 2-tuple, or a callable. A 2-tuple defines the +extension length on both ends of the path. A callable must accept +4 arguments (cap end point and direction for both path sides) and +return a sequence of points that define the cap shape.
tolerance – Tolerance used for calculating the polygonal +approximation of the paths.
max_evals – Maximal number of path evaluations per section. Used for +intersection finding at joins and to create the polygonal +approximation for each section.
simple_path – If True
, the paths will be stored as GDSII path
+elements. They require less memory, but do not support “smooth” or
+callable joins and end caps, or width changes along the path.
scale_width – If False
, the path widths are not scaled when
+transforming this path.
layer – layer number assigned to this path.
datatype – data type number assigned to this path.
Notes
+If width
is a number and offset
a sequence, the number of
+parallel paths is defined by the latter.
Arguments ends
, layer
, and datatype
can also be lists
+with one definition for each path created.
Examples
+>>> fpath = gdstk.FlexPath((0, 0), 0.5, tolerance=1e-3)
+>>> fpath.arc(1, 0, numpy.pi/2)
+>>> fpath.arc(1, 0, -numpy.pi/2)
+>>> rpath = gdstk.RobustPath((3, 0), 0.5, tolerance=1e-3)
+>>> rpath.arc(1, 0, numpy.pi/2)
+>>> rpath.arc(1, 0, -numpy.pi/2)
+
>>> path = gdstk.RobustPath((0, 0),
+... [0.8, 0.8, 0.8, 0.8],
+... 1.0,
+... ends=["flush", "extended", (0.4, 0.8), "round"],
+... tolerance=1e-3,
+... layer=[0, 1, 2, 3],
+... )
+>>> path.horizontal(5)
+
Methods
++ | Create new robustpaths based on this object's |
+
|
+Append an elliptical arc to this path. |
+
|
+Append a Bézier curve to this path. |
+
|
+Append sections to this path according to commands. |
+
|
+Create a copy this robustpath. |
+
|
+Append a cubic Bézier curve to this path. |
+
|
+Append a smooth cubic Bézier curve to this path. |
+
|
+Delete a GDSII property of this element. |
+
|
+Delete the first property of this element matching a name. |
+
|
+Return a GDSII property of this element. |
+
|
+Return the values of the first property of this element matching a name. |
+
|
+Calculate the gradient of this path. |
+
|
+Append a horizontal segment to this path. |
+
|
+Append a smooth interpolating curve through the given points. |
+
|
+Mirror this path across the line through p1 and p2. |
+
|
+Calculate the offset of this path. |
+
|
+Append a parametric curve to this path. |
+
+ | Central spines of each parallel path. |
+
|
+Calculate the position of this path. |
+
|
+Append a quadratic Bézier curve to this path. |
+
|
+Append a smooth quadratic Bézier curve to this path. |
+
|
+Rotate this path. |
+
|
+Scale this path. |
+
|
+Append a straight segment to this path. |
+
|
+Set the datatypes for all paths. |
+
|
+Set the end types for all paths. |
+
|
+Set a GDSII property for this element. |
+
|
+Set the layers for all paths. |
+
|
+Set a property for this element. |
+
|
+Central path spine. |
+
+ | Calculate the polygonal representations of this path. |
+
|
+Translate this path. |
+
|
+Append a circular turn to this path. |
+
|
+Append a vertical segment to this path. |
+
|
+Calculate the width of this path. |
+
Attributes
++ | RobustPath data type. |
+
+ | End types for each path. |
+
+ | RobustPath layer. |
+
+ | Maximal number of path evaluations per section. |
+
+ | Number of paths. |
+
+ | Properties of this element. |
+
+ | Repetition associated with this element. |
+
+ | Scale width flag. |
+
+ | Simple path flag. |
+
+ | Number of sections in this path. |
+
+ | Path tolerance. |
+
Create new robustpaths based on this object’s repetition
attribute.
After the repetition is applied, the original attribute is set to
+None
.
Newly created objects.
+Append an elliptical arc to this path.
+radius (number or sequence[2]) – Circular arc radius or elliptical +arc radii.
initial_angle – Starting angle (in radians).
final_angle – Ending angle (in radians).
rotation – Arc rotation.
width (number, tuple, or callable) – Width of this section. A tuple +of 2 elements (number, str) can be used to define the width value +and the interpolation type from the previous section to the end +point (“constant”, “linear”, or “smooth”). If only a number is +used, the interpolation defaults to “linear”. A callable must +accept a single number from 0 to 1 and return the desired width at +the corresponding position along the section.
offset (number, tuple, or callable) – Offset from the central path
+position. A tuple or callable can be used as in width
.
Examples
+>>> path = gdstk.FlexPath((0, 0), [0.2, 0.3], 0.4, tolerance=1e-3)
+>>> path.vertical(5)
+>>> path.arc(2.5, numpy.pi, 0)
+>>> path.arc((3, 5), -numpy.pi, -numpy.pi / 2)
+
Append a Bézier curve to this path.
+xy (sequence of points) – Control points for the Bézier. Each point +can be a coordinate pair or a complex.
width (number, tuple, or callable) – Width of this section. A tuple +of 2 elements (number, str) can be used to define the width value +and the interpolation type from the previous section to the end +point (“constant”, “linear”, or “smooth”). If only a number is +used, the interpolation defaults to “linear”. A callable must +accept a single number from 0 to 1 and return the desired width at +the corresponding position along the section.
offset (number, tuple, or callable) – Offset from the central path
+position. A tuple or callable can be used as in width
.
relative – If True
, coordinates are relative to the last point.
Notes
+Arguments width
and offset
can also be lists with one
+definition for each path.
If offset
is a single number or tuple, it is treated as the
+distance between adjacent paths.
Examples
+>>> path = gdstk.RobustPath((0, 0), 0.2, tolerance=1e-3)
+>>> path.bezier(
+... [(4, 1), (4, 3), (0, 5), (-4, 3), (-4, -2), (0, -4)],
+... width=lambda u: 0.2 + 0.8 * u ** 2,
+... )
+
See also
+ +Append sections to this path according to commands.
+Commands are single characters followed by a pre-defined number of +numerical arguments, according to the table below:
+Command |
+Primitive |
+Arguments |
+
---|---|---|
L/l |
+Line segment |
+x, y |
+
H/h |
+Horizontal segment |
+x |
+
V/v |
+Vertical segment |
+y |
+
C/c |
+Cubic Bézier |
+x0, y0, x1, y1, x2, y2 |
+
S/s |
+Smooth cubic Bézier |
+x0, y0, x1, y1 |
+
Q/q |
+Quadratic Bézier |
+x0, y0, x1, y1 |
+
T/t |
+Smooth quadratic Bézier |
+x, y |
+
a |
+Circular turn |
+rad, ang |
+
A |
+Circular arc |
+rad, ang0, ang1 |
+
E |
+Elliptical arc |
+rad0, rad1, ang0, ang1, rot |
+
Uppercase commands assume that coordinates are absolute, whereas the +lowercase versions assume they are relative to the previous endpoint.
+Notes
+The meaning and order of the arguments of all commands are identical +to the corresponding method.
+Examples
+>>> path = gdstk.RobustPath((0, 0), [0.2, 0.4, 0.2], 0.5,
+... tolerance=1e-3)
+>>> path.commands("l", 3, 4,
+... "A", 2, numpy.arctan2(3, -4), numpy.pi / 2,
+... "h", 0.5,
+... "a", 3, -numpy.pi)
+
Create a copy this robustpath.
+Copy of this robustpath.
+Append a cubic Bézier curve to this path.
+xy (sequence of 3 points) – Control points and end point for the +Bézier. Each point can be a coordinate pair or a complex.
width (number, tuple, or callable) – Width of this section. A tuple +of 2 elements (number, str) can be used to define the width value +and the interpolation type from the previous section to the end +point (“constant”, “linear”, or “smooth”). If only a number is +used, the interpolation defaults to “linear”. A callable must +accept a single number from 0 to 1 and return the desired width at +the corresponding position along the section.
offset (number, tuple, or callable) – Offset from the central path
+position. A tuple or callable can be used as in width
.
relative – If True
, coordinates are relative to the last point.
Notes
+Arguments width
and offset
can also be lists with one
+definition for each path.
If offset
is a single number or tuple, it is treated as the
+distance between adjacent paths.
Examples
+>>> path = gdstk.RobustPath((0, 0), 0.2, tolerance=1e-3)
+>>> path.cubic([(0.5, 0.8), (0.5, 2.2), (0, 3)])
+>>> path.cubic([(0.8, -0.5), (2.2, -0.5), (3, 0)], relative=True)
+>>> path.cubic([(-0.5, -0.8), (-0.5, -2.2), (0, -3)], relative=True)
+
See also
+ +Append a smooth cubic Bézier curve to this path.
+The first control point of the Bézier is such that the gradient of the +path is continuous.
+xy (sequence of 2 points) – Second control point and end point for +the Bézier. Each can be a coordinate pair or a complex.
width (number, tuple, or callable) – Width of this section. A tuple +of 2 elements (number, str) can be used to define the width value +and the interpolation type from the previous section to the end +point (“constant”, “linear”, or “smooth”). If only a number is +used, the interpolation defaults to “linear”. A callable must +accept a single number from 0 to 1 and return the desired width at +the corresponding position along the section.
offset (number, tuple, or callable) – Offset from the central path
+position. A tuple or callable can be used as in width
.
relative – If True
, coordinates are relative to the last point.
Notes
+Arguments width
and offset
can also be lists with one
+definition for each path.
If offset
is a single number or tuple, it is treated as the
+distance between adjacent paths.
Examples
+>>> path = gdstk.RobustPath((0, 0), 0.2, tolerance=1e-3)
+>>> path.cubic([(0.5, 0.8), (0.5, 2.2), (0, 3)])
+>>> path.cubic_smooth([(3.5, 0.8), (3, 0)], relative=True)
+>>> path.cubic_smooth([(-0.5, -2.2), (0, -3)], relative=True)
+
See also
+ +RobustPath data type.
+Notes
+This attribute is read-only.
+See also
+ +Delete a GDSII property of this element.
+attr (number) – Property number.
+Delete the first property of this element matching a name.
+name (str) – Property name.
+End types for each path.
+Notes
+This attribute is read-only.
+See also
+ +Return a GDSII property of this element.
+attr (number) – Property number.
+Property value. If the property number does not exist,
+None
is returned.
str or None
+Return the values of the first property of this element matching a name.
+name (str) – Property name.
+List of property values. If no property is found,
+None
is returned.
list or None
+Calculate the gradient of this path.
+u (number) – Where in the path to calculate the position. The value
+must be in the range 0 thru gdstk.RobustPath.size
,
+inclusive.
from_below – If there is a discontinuity at u
, use the value
+approaching from below.
The spine gradient of the path at u
.
Append a horizontal segment to this path.
+x (number) – End point x coordinate.
width (number, tuple, or callable) – Width of this section. A tuple +of 2 elements (number, str) can be used to define the width value +and the interpolation type from the previous section to the end +point (“constant”, “linear”, or “smooth”). If only a number is +used, the interpolation defaults to “linear”. A callable must +accept a single number from 0 to 1 and return the desired width at +the corresponding position along the section.
offset (number, tuple, or callable) – Offset from the central path
+position. A tuple or callable can be used as in width
.
relative – If True
, coordinates are relative to the last point.
Notes
+Arguments width
and offset
can also be lists with one
+definition for each path.
If offset
is a single number or tuple, it is treated as the
+distance between adjacent paths.
See also
+ +Append a smooth interpolating curve through the given points.
+Use the Hobby algorithm [1] to calculate a smooth interpolating +path made of cubic Bezier segments between each pair of points. Angle +and tension parameters can be specified at each point, and the path can +be open or closed.
+points (sequence[N] of points) – Vertices in the interpolating path.
angles (None
or sequence[N + 1]) – Tangent angles at each point
+(in radians). Angles defined as None
are automatically
+calculated.
tension_in (number or sequence[N + 1]) – Tension parameter when +arriving at each point. One value per point or a single value used +for all points.
tension_out (number or sequence[N + 1]) – Tension parameter when +leaving each point. One value per point or a single value used for +all points.
initial_curl – Ratio between the mock curvatures at the first point +and at its neighbor. A value of 1 renders the first segment a good +approximation for a circular arc. A value of 0 will better +approximate a straight segment. It has no effect for closed paths +or when an angle is defined for the first point.
final_curl – Ratio between the mock curvatures at the last point and +at its neighbor. It has no effect for closed paths or when an +angle is defined for the first point.
cycle – If True
, calculates control points for a closed path,
+with an additional segment connecting the first and last points.
width (number, tuple, or callable) – Width of this section. A tuple +of 2 elements (number, str) can be used to define the width value +and the interpolation type from the previous section to the end +point (“constant”, “linear”, or “smooth”). If only a number is +used, the interpolation defaults to “linear”. A callable must +accept a single number from 0 to 1 and return the desired width at +the corresponding position along the section.
offset (number, tuple, or callable) – Offset from the central path
+position. A tuple or callable can be used as in width
.
relative – If True
, coordinates are relative to the last point.
Examples
+>>> half_pi = numpy.pi / 2
+>>> points = [(4, 1), (4, 3), (0, 5), (-4, 3), (-4, -2), (0, -4)]
+>>> angles = [half_pi, None, None, None, -half_pi, -half_pi, None]
+>>> path_1 = gdstk.RobustPath((0, 0), 0.2, tolerance=1e-3)
+>>> path_1.interpolation(points, cycle=True)
+>>> path_2 = gdstk.RobustPath((6, -8), 0.2, tolerance=1e-3)
+>>> path_2.interpolation(points, angles, cycle=True, relative=True)
+
See also
+ +Hobby, J.D. “Smooth, easy to compute interpolating splines.” +Discrete Comput Geom 1, 123–140 (1986). DOI: 10.1007/BF02187690.
+RobustPath layer.
+Notes
+This attribute is read-only.
+See also
+ +Maximal number of path evaluations per section.
+Mirror this path across the line through p1 and p2.
+p1 (coordinate pair or complex) – First point in the mirror line.
p2 (coordinate pair or complex) – Second point in the mirror line.
Number of paths.
+Notes
+This attribute is read-only.
+Calculate the offset of this path.
+u (number) – Where in the path to calculate the width. The value must
+be in the range 0 thru gdstk.RobustPath.size
, inclusive.
from_below – If there is a discontinuity at u
, use the value
+approaching from below.
The offsets of each path at u
.
Append a parametric curve to this path.
+path_function (callable) – Function that defines the path. Must be a +function of one argument (that varies from 0 to 1) that returns a +2-element sequence or complex with the coordinates of the path.
path_gradient (callable) – Function that returns the path gradient. +Must be a function of one argument (that varies from 0 to 1) that +returns a 2-element sequence or complex with the gradient.
width (number, tuple, or callable) – Width of this section. A tuple +of 2 elements (number, str) can be used to define the width value +and the interpolation type from the previous section to the end +point (“constant”, “linear”, or “smooth”). If only a number is +used, the interpolation defaults to “linear”. A callable must +accept a single number from 0 to 1 and return the desired width at +the corresponding position along the section.
offset (number, tuple, or callable) – Offset from the central path
+position. A tuple or callable can be used as in width
.
relative – If True
, the return values of path_function
are
+used as offsets from the current path position, i.e., to ensure a
+continuous path, path_function(0)
must be (0, 0). Otherwise,
+they are used as absolute coordinates.
Examples
+>>> def spiral(u):
+... rad = 2 * u ** 0.5
+... ang = 3 * numpy.pi * u
+... return (rad * numpy.cos(ang), rad * numpy.sin(ang))
+>>> path = gdstk.RobustPath((0, 0), 0.2, tolerance=1e-3)
+>>> path.parametric(spiral, width=lambda u: 0.2 + 0.6 * u ** 2)
+
Central spines of each parallel path.
+Copy of the points that make up each parallel path.
+Calculate the position of this path.
+u (number) – Where in the path to calculate the position. The value
+must be in the range 0 thru gdstk.RobustPath.size
,
+inclusive.
from_below – If there is a discontinuity at u
, use the value
+approaching from below.
The spine position of the path at u
.
Properties of this element.
+Properties are represented as a list of lists, each containing the +property name followed by its values.
+Append a quadratic Bézier curve to this path.
+xy (sequence of 2 points) – Control point and end point for the +Bézier. Each point can be a coordinate pair or a complex.
width (number, tuple, or callable) – Width of this section. A tuple +of 2 elements (number, str) can be used to define the width value +and the interpolation type from the previous section to the end +point (“constant”, “linear”, or “smooth”). If only a number is +used, the interpolation defaults to “linear”. A callable must +accept a single number from 0 to 1 and return the desired width at +the corresponding position along the section.
offset (number, tuple, or callable) – Offset from the central path
+position. A tuple or callable can be used as in width
.
relative – If True
, coordinates are relative to the last point.
Notes
+Arguments width
and offset
can also be lists with one
+definition for each path.
If offset
is a single number or tuple, it is treated as the
+distance between adjacent paths.
See also
+ +Append a smooth quadratic Bézier curve to this path.
+The control point of the Bézier is such that the gradient of the path is +continuous.
+xy (coordinate pair or complex) – End point for the Bézier.
width (number, tuple, or callable) – Width of this section. A tuple +of 2 elements (number, str) can be used to define the width value +and the interpolation type from the previous section to the end +point (“constant”, “linear”, or “smooth”). If only a number is +used, the interpolation defaults to “linear”. A callable must +accept a single number from 0 to 1 and return the desired width at +the corresponding position along the section.
offset (number, tuple, or callable) – Offset from the central path
+position. A tuple or callable can be used as in width
.
relative – If True
, coordinates are relative to the last point.
Notes
+Arguments width
and offset
can also be lists with one
+definition for each path.
If offset
is a single number or tuple, it is treated as the
+distance between adjacent paths.
See also
+gdstk.RobustPath.segment()
,
+gdstk.RobustPath.cubic_smooth()
Repetition associated with this element.
+Rotate this path.
+angle – Rotation angle (in radians).
center (coordinate pair or complex) – Center of the transformation.
Scale this path.
+s – Scaling factor.
center (coordinate pair or complex) – Center of the transformation.
Scale width flag.
+Append a straight segment to this path.
+xy (coordinate pair or complex) – Segment end point.
width (number, tuple, or callable) – Width of this section. A tuple +of 2 elements (number, str) can be used to define the width value +and the interpolation type from the previous section to the end +point (“constant”, “linear”, or “smooth”). If only a number is +used, the interpolation defaults to “linear”. A callable must +accept a single number from 0 to 1 and return the desired width at +the corresponding position along the section.
offset (number, tuple, or callable) – Offset from the central path
+position. A tuple or callable can be used as in width
.
relative – If True
, coordinates are relative to the last point.
Notes
+Arguments width
and offset
can also be lists with one
+definition for each path.
If offset
is a single number or tuple, it is treated as the
+distance between adjacent paths.
Examples
+>>> path = gdstk.RobustPath((0, 0), 0.2, tolerance=1e-3)
+>>> path.segment((5, 0), 1.0)
+>>> path.segment((5, 3), (0.2, "constant"))
+>>> path.segment((0, 3), (1.2, "smooth"))
+>>> path.segment(
+... (0, 7),
+... lambda u: 0.2 + 0.8 * numpy.cos(2 * numpy.pi * u) ** 2
+... )
+
>>> path = gdstk.RobustPath((0, 0), [0.2, 0.2], 0.3, tolerance=1e-3)
+>>> path.segment((5, 0), offset=0.8)
+>>> path.segment((5, 3), offset=(0.3, "constant"))
+>>> path.segment((0, 3), offset=(1.0, "smooth"))
+>>> path.segment((0, 7), offset=[
+... lambda u: -0.2 - 0.8 * numpy.cos(2 * numpy.pi * u) ** 2,
+... lambda u: 0.2 + 0.8 * numpy.cos(2 * numpy.pi * u) ** 2
+... ])
+
Set the datatypes for all paths.
+datatypes – data type numbers for all paths.
+Set the end types for all paths.
+ends – Sequence of “flush”, “extended”, “round”, “smooth”, a 2-tuple, +or a callable.
+See also
+ +Set a GDSII property for this element.
+GDSII properties are stored under the special name “S_GDS_PROPERTY”, as +defined by the OASIS specification.
+attr (number) – Property number.
value (str) – Property value.
Set the layers for all paths.
+layers – layer numbers for all paths.
+Set a property for this element.
+The property name does not have to be unique. Multiple properties can +have the same name.
+name (str) – Property name.
value (str, bytes, number, or sequence of those) – Values associated +with the property.
Notes
+These properties can be used to associate custom metadata with an +element, but general properties are not supported by GDSII files, +only OASIS. Use the specific methods to access GDSII properties.
+Simple path flag.
+Number of sections in this path.
+Notes
+This attribute is read-only.
+Central path spine.
+Copy of the points that make up the path at zero offset.
+Calculate the polygonal representations of this path.
+The polygonal contours defined by this path.
+Path tolerance.
+Translate this path.
+dx – Translation in the x coordinate or translation vector.
dy – Translation in the y coordinate.
Append a circular turn to this path.
+radius – Circular arc radius.
angle – Turning angle. Positive values turn counter clockwise and +negative values, clockwise.
width (number or sequence) – Width at the end point(s). If this is a +sequence, it must define the width for each path. The width is +linearly tapered from its previous value.
offset (number or sequence) – If this is a number, it is treated as +the distance between centers of adjacent paths. If it is a +sequence, each number represents the absolute offset from the +center of the path. The offsets are linearly tapered from their +previous values.
Append a vertical segment to this path.
+y (number) – End point x coordinate.
width (number, tuple, or callable) – Width of this section. A tuple +of 2 elements (number, str) can be used to define the width value +and the interpolation type from the previous section to the end +point (“constant”, “linear”, or “smooth”). If only a number is +used, the interpolation defaults to “linear”. A callable must +accept a single number from 0 to 1 and return the desired width at +the corresponding position along the section.
offset (number, tuple, or callable) – Offset from the central path
+position. A tuple or callable can be used as in width
.
relative – If True
, coordinates are relative to the last point.
Notes
+Arguments width
and offset
can also be lists with one
+definition for each path.
If offset
is a single number or tuple, it is treated as the
+distance between adjacent paths.
See also
+ +Calculate the width of this path.
+u (number) – Where in the path to calculate the width. The value must
+be in the range 0 thru gdstk.RobustPath.size
, inclusive.
from_below – If there is a discontinuity at u
, use the value
+approaching from below.
The widths of each path at u
.
Check whether all points are inside the set of polygons.
+Points on polygon edges or coinciding with any of their vertices are +considered inside.
+points – Points to check. Each point can be a pair of coordinates or +a complex number.
polygons (Polygon, FlexPath, RobustPath, Reference, sequence) – Polygons to test against. If this is a sequence, each element can +be any of the polygonal types or a sequence of points (coordinate +pairs or complex).
True if all points are inside the polygon set, False otherwise.
+Check whether any of the points are inside the set of polygons.
+Points on polygon edges or coinciding with any of their vertices are +considered inside.
+points – Points to check. Each point can be a pair of coordinates or +a complex number.
polygons (Polygon, FlexPath, RobustPath, Reference, sequence) – Polygons to test against. If this is a sequence, each element can +be any of the polygonal types or a sequence of points (coordinate +pairs or complex).
True if any point is inside the polygon set, False otherwise.
+Execute boolean (clipping) operations between polygons.
+operand1 (Polygon, FlexPath, RobustPath, Reference, sequence) – First +operand. If this is a sequence, each element can be any of the +polygonal types or a sequence of points (coordinate pairs or +complex).
operand2 (Polygon, FlexPath, RobustPath, Reference, sequence) – Second operand.
operation (str) – Boolean operation to be executed. One of “or”,
+“and”, “xor”, or “not”. The “not” operation performs the set
+difference operand1 - operand2
.
precision – Desired precision for rounding vertex coordinates.
layer – layer number assigned to the resulting polygons.
datatype – data type number assigned to the resulting polygons.
List of gdstk.Polygon
.
Examples
+>>> circle = gdstk.ellipse((0, 0), 50)
+>>> path = gdstk.FlexPath((-50, 30), [5, 5], 10)
+>>> path.interpolation([(20, 15), (0, 0), (-20, -15), (50, -30)],
+... angles=[0.3, None, None, None, 0.3])
+>>> text = gdstk.text("GDSTK", 40, (-2.5 * 40 * 9 / 16, -40 / 2))
+>>> result = gdstk.boolean(circle, text + [path], "not")
+
Notes
+Repetitions are not applied to any elements, except references +and their contents.
All polygons in operand1 are merged during the operation, as +well as those in operand2. As such, if, for example, +operand2 is an empty list, the result of the operation will be +the union of polygons in operand1.
Extract polygonal contours from 2-d array data at given level.
+data (array-like[M][N]) – 2-dimensional array with shape (M, N).
level – Polygons are created representing the regions where data is +at least level.
length_scale – Distance between neighboring elements in data.
precision – Desired precision for rounding vertex coordinates.
layer – layer number assigned to the resulting polygons.
datatype – data type number assigned to the resulting polygons.
List of gdstk.Polygon
.
Examples
+>>> y, x = numpy.mgrid[0.5:1.5:128j, -1:1:255j]
+>>> data = (numpy.sin(4 * numpy.pi * x * y)
+... - 0.8 * numpy.sin(2 * numpy.pi * y))
+>>> contours = gdstk.contour(data, 0, 1 / 127, 0.01 / 127)
+>>> rect = gdstk.rectangle((0, 0), (2, 1), datatype=1)
+
Notes
+The length scale for the polygons is one element of data, i.e., +the full region represented by data represents a rectangular area +of lenght_scale * (N - 1) × length_scale * (M - 1). Argument +precision is understood in this length scale.
+Create a cross shape.
+center (coordinate pair or complex) – Center of the cross.
full_size (number) – Total length of the cross.
arm_width (number) – Width of the arms.
layer – layer number assigned to this polygon.
datatype – data type number assigned to this polygon.
Examples
+>>> cross1 = gdstk.cross((0, 0), 10, 1)
+>>> cross2 = gdstk.cross((0.5, 0.5), 5, 0.75, layer=1)
+
Create an ellipse, circle, slice or ring.
+center (coordinate pair or complex) – Circle/ellipse center.
radius (number or sequence) – Circle radius of the circle. A pair of +numbers can be used to define an ellipse.
inner_radius (number or sequence) – If set, creates a ring.
initial_angle – Initial angle to define a slice.
final_angle – Final slice angle.
tolerance – Tolerance used for calculating the polygonal +approximation of this shape.
layer – layer number assigned to this polygon.
datatype – data type number assigned to this polygon.
Examples
+>>> circle = gdstk.ellipse((0, 0), 40)
+>>> ellipse = gdstk.ellipse((100, 0), (40, 30), layer=1)
+>>> ring = gdstk.ellipse((0, 100), 40, inner_radius=(30, 20),
+ layer=2)
+>>> c_slice = gdstk.ellipse((100, 100), 40,
+... initial_angle=-numpy.pi / 4,
+... final_angle=5 * numpy.pi / 4,
+... layer=3)
+>>> r_slice = gdstk.ellipse((50, 200), (70, 30), (60, 20),
+... -3 * numpy.pi / 4, numpy.pi / 2,
+... layer=4)
+
Check whether each point is inside the set of polygons.
+Points on polygon edges or coinciding with any of their vertices are +considered inside.
+points – Points to check. Each point can be a pair of coordinates or +a complex number.
polygons (Polygon, FlexPath, RobustPath, Reference, sequence) – Polygons to test against. If this is a sequence, each element can +be any of the polygonal types or a sequence of points (coordinate +pairs or complex).
Tuple of booleans (one for each point).
+Dilate or erode polygons.
+polygons (Polygon, FlexPath, RobustPath, Reference, sequence) – Polygons to offset. If this is a sequence, each element can be any +of the polygonal types or a sequence of points (coordinate pairs +or complex).
distance (number) – Offset distance. Positive values dilate the +geometry, whereas negative values erode it.
join – Type of joins for the offset polygon. One of “miter”, “bevel”, +or “round”.
tolerance – For miter joints, this number must be at least 2 and it +represents the maximal distance in multiples of offset between new +vertices and their original position before beveling to avoid +spikes at acute joints. For round joints, it indicates the +curvature resolution in number of points per full circle.
precision – Desired precision for rounding vertex coordinates.
use_union – Apply a union operation before the offset to merge +adjacent polygons.
layer – layer number assigned to the resulting polygons.
datatype – data type number assigned to the resulting polygons.
List of gdstk.Polygon
.
Examples
+>>> text = gdstk.text("#A", 10, (0, 0), datatype=1)
+>>> circle = gdstk.ellipse((5, 11), 5, initial_angle=0,
+... final_angle=numpy.pi, datatype=1)
+>>> path = gdstk.FlexPath([(0, -1), (5, -10), (10, -1)], 1,
+>>> datatype=1)
+>>> dilated = gdstk.offset(text + [circle, path], 0.4)
+>>> eroded = gdstk.offset(text + [circle, path], -0.4,
+... use_union=True, layer=1)
+
Notes
+Repetitions are not applied to any elements, except references and +their contents.
+Create a racetrack shape.
+center (coordinate pair or complex) – Polygon center.
straight_length (number) – Length of the straight segment.
radius (number) – Radius of the circular arc.
inner_radius (number) – If greater than 0, create a racetrack ring.
vertical – If True
, the racetrack is created vertically.
tolerance – Tolerance used for calculating the polygonal +approximation for the circular arcs.
layer – layer number assigned to this polygon.
datatype – data type number assigned to this polygon.
Examples
+>>> racetrack1 = gdstk.racetrack((0, 0), 8, 5)
+>>> racetrack2 = gdstk.racetrack((18, 0), 8, 5, 3, True)
+
Create a rectangle.
+corner1 (coordinate pair or complex) – First rectangle corner.
corner2 (coordinate pair or complex) – Opposing corner.
layer – layer number assigned to this polygon.
datatype – data type number assigned to this polygon.
Create a regular polygon.
+center (coordinate pair or complex) – Polygon center.
side_length (number) – Length of the sides of the polygon.
sides (integer) – Number of polygon sides.
rotation – Rotation angle (in radians).
layer – layer number assigned to this polygon.
datatype – data type number assigned to this polygon.
Examples
+>>> poly3 = gdstk.regular_polygon((0, 0), 9, 3)
+>>> poly4 = gdstk.regular_polygon((10, 0), 7, 4, layer=1)
+>>> poly5 = gdstk.regular_polygon((0, 10), 5, 5, layer=2)
+>>> poly6 = gdstk.regular_polygon((10, 10), 4, 6, layer=3)
+
Slice polygons along x and y axes.
+polygons (Polygon, FlexPath, RobustPath, Reference, sequence) – Polygons to offset. If this is a sequence, each element can be any +of the polygonal types or a sequence of points (coordinate pairs +or complex).
position (number or sequence) – Cut positions.
axis (str) – One of “x” or “y”. Indicated the axis where positions +are specified. The cut direction is orthogonal to this axis.
precision – Desired precision for rounding vertex coordinates.
List with N + 1 elements, where N is the number of cut positions. +Each element is a list of polygons obtained between cuts.
+Examples
+>>> triangle = gdstk.regular_polygon((-10, 0), 8, 3)
+>>> ring = gdstk.ellipse((10, 0), 8, 5, layer=1)
+>>> result = gdstk.slice([triangle, ring], (-10, -5, 0, 6, 14), "x")
+>>> print(len(result))
+6
+>>> print([len(polys) for polys in result])
+[1, 1, 0, 1, 2, 1]
+
Notes
+Repetitions are not applied to any elements, except references and +their contents.
+Create polygonal text.
+text (str) – Text string.
size (number) – Full height of the font.
position (coordinate pair or complex) – Text starting position.
vertical – Writing direction.
layer – layer number assigned to the polygons.
datatype – data type number assigned to the polygons.
List of gdstk.Polygon
.
Examples
+>>> text = gdstk.text(f"Created with\nGDSTK {gdstk.__version__}", 1,
+... (0, 0))
+>>> rect = gdstk.rectangle((0, -5 / 4), (12 * 9 / 16, 1),
+... datatype=1)
+
Notes
+The character aspect ratio is 1:2. For horizontal text, spacings
+between characters and between lines are 9 / 16 and 5 / 4 times the
+full height size
, respectively. For vertical text, characters
+and columns are respectively spaced by 9 / 8 and 1 times size
.
GDSII files contain a hierarchical representation of any polygonal geometry. +They are mainly used in the microelectronics industry for the design of mask +layouts, but are also employed in other areas.
+Because it is a hierarchical format, repeated structures, such as identical +transistors, can be defined once and referenced multiple times in the layout, +reducing the file size.
+There is one important limitation in the GDSII format: it only supports weakly +simple polygons, that is, +polygons whose segments are allowed to intersect, but not cross.
+In particular, curves and shapes with holes are not directly supported. +Holes can be defined, nonetheless, by connecting their boundary to the boundary +of the enclosing shape. In the case of curves, they must be approximated by a +polygon. The number of points in the polygonal approximation can be increased +to better approximate the original curve up to some acceptable error.
+The original GDSII format limits the number of vertices in a polygon to 199. +This limit seems arbitrary, as the maximal number of vertices that can be +stored in a GDSII record is 8190. Nonetheless, most modern software disregard +both limits and allow an arbitrary number of points per polygon. Gdstk follows +the modern version of GDSII, but this is an important issue to keep in mind if +the generated file is to be used in older systems.
+The units used to represent shapes in the GDSII format are defined by the user. +The default unit in Gdstk is 1 µm (10⁻⁶ m), but that can be easily changed by +the user.
+Let’s create our first layout file:
+import gdstk
+
+# The GDSII file is called a library, which contains multiple cells.
+lib = gdstk.Library()
+
+# Geometry must be placed in cells.
+cell = lib.new_cell("FIRST")
+
+# Create the geometry (a single rectangle) and add it to the cell.
+rect = gdstk.rectangle((0, 0), (2, 1))
+cell.add(rect)
+
+# Save the library in a GDSII or OASIS file.
+lib.write_gds("first.gds")
+lib.write_oas("first.oas")
+
+# Optionally, save an image of the cell as SVG.
+cell.write_svg("first.svg")
+
#include <stdio.h>
+
+#include <gdstk/gdstk.hpp>
+
+using namespace gdstk;
+
+int main(int argc, char* argv[]) {
+ Library lib = {};
+ lib.init("library", 1e-6, 1e-9);
+
+ Cell cell = {};
+ cell.name = copy_string("FIRST", NULL);
+ lib.cell_array.append(&cell);
+
+ Polygon rect = rectangle(Vec2{0, 0}, Vec2{2, 1}, make_tag(0, 0));
+ cell.polygon_array.append(&rect);
+
+ lib.write_gds("first.gds", 0, NULL);
+ lib.write_oas("first.oas", 0, 6, OASIS_CONFIG_DETECT_ALL);
+ cell.write_svg("first.svg", 10, 6, NULL, NULL, "#222222", 5, true, NULL);
+
+ rect.clear();
+ cell.clear();
+ lib.clear();
+ return 0;
+}
+
After importing the gdstk
module, we create a library lib
to hold the
+design.
All layout elements must be added to cells, which can be though of pieces of +papers where the geometry is drawn. Later, a cell can reference others to +build hierarchical designs, as if stamping the referenced cell into the first, +as we’ll see in References.
+We create a gdstk.Cell
with name “FIRST” (cells are identified by
+name, therefore cell names must be unique within a library) and add a
+gdstk.rectangle()
to it.
Finally, the whole library is saved in a GDSII file called “first.gds” and an +OASIS file “first.oas” in the current directory. The GDSII or OASIS files can +be opened in a number of viewers and editors, such as KLayout.
+General polygons can be defined by an ordered list of vertices. The +orientation of the vertices (clockwise/counter-clockwise) is not important: +they will be ordered internally.
+# Create a polygon from a list of vertices
+points = [(0, 0), (2, 2), (2, 6), (-6, 6), (-6, -6), (-4, -4), (-4, 4), (0, 4)]
+poly = gdstk.Polygon(points)
+
void example_polygons(Cell& out_cell) {
+ Vec2 points[] = {{0, 0}, {2, 2}, {2, 6}, {-6, 6}, {-6, -6}, {-4, -4}, {-4, 4}, {0, 4}};
+
+ // This has to be heap-allocated so that it doesn't go out of scope once
+ // the function returns. We also don't worry about leaking it at the end
+ // of the program. The OS will take care of it.
+ Polygon* poly = (Polygon*)allocate_clear(sizeof(Polygon));
+ poly->point_array.extend({.capacity = 0, .count = COUNT(points), .items = points});
+ out_cell.polygon_array.append(poly);
+}
+
As mentioned in Getting Started, holes have to be connected to the outer +boundary of the polygon, as in the following example:
+# Manually connect the hole to the outer boundary
+cutout = gdstk.Polygon(
+ [(0, 0), (5, 0), (5, 5), (0, 5), (0, 0), (2, 2), (2, 3), (3, 3), (3, 2), (2, 2)]
+)
+
void example_holes(Cell& out_cell) {
+ Vec2 points[] = {{0, 0}, {5, 0}, {5, 5}, {0, 5}, {0, 0},
+ {2, 2}, {2, 3}, {3, 3}, {3, 2}, {2, 2}};
+ Polygon* poly = (Polygon*)allocate_clear(sizeof(Polygon));
+ poly->point_array.extend({.capacity = 0, .count = COUNT(points), .items = points});
+ out_cell.polygon_array.append(poly);
+}
+
The gdstk.ellipse()
function creates circles, ellipses, doughnuts, arcs
+and slices. In all cases, the argument tolerance
will control the number
+of vertices used to approximate the curved shapes.
When saving a library with gdstk.Library.write_gds()
, if the number of
+vertices in the polygon is larger than max_points
(199 by default), it will
+be fractured in many smaller polygons with at most max_points
vertices
+each.
OASIS files don’t have a limit to the number of polygon vertices, so no +polygons are fractured when saving. They also have support for circles. When +saving an OASIS file, polygonal circles will be detected within a predefined +tolerance and automatically converted.
+# Circle centered at (0, 0), with radius 2 and tolerance 0.1
+circle = gdstk.ellipse((0, 0), 2, tolerance=0.01)
+
+# To create an ellipse, simply pass a list with 2 radii.
+# Because the tolerance is small (resulting a large number of
+# vertices), the ellipse is fractured in 2 polygons.
+ellipse = gdstk.ellipse((4, 0), [1, 2], tolerance=1e-4)
+
+# Circular arc example
+arc = gdstk.ellipse(
+ (2, 4),
+ 2,
+ inner_radius=1,
+ initial_angle=-0.2 * numpy.pi,
+ final_angle=1.2 * numpy.pi,
+ tolerance=0.01,
+)
+
void example_circles(Cell& out_cell) {
+ Polygon* circle = (Polygon*)allocate_clear(sizeof(Polygon));
+ *circle = ellipse(Vec2{0, 0}, 2, 2, 0, 0, 0, 0, 0.01, 0);
+ out_cell.polygon_array.append(circle);
+
+ Polygon* ellipse_ = (Polygon*)allocate_clear(sizeof(Polygon));
+ *ellipse_ = ellipse(Vec2{4, 0}, 1, 2, 0, 0, 0, 0, 1e-4, 0);
+ out_cell.polygon_array.append(ellipse_);
+
+ Polygon* arc = (Polygon*)allocate_clear(sizeof(Polygon));
+ *arc = ellipse(Vec2{2, 4}, 2, 2, 1, 1, -0.2 * M_PI, 1.2 * M_PI, 0.01, 0);
+ out_cell.polygon_array.append(arc);
+}
+
Constructing complex polygons by manually listing all vertices in
+gdstk.Polygon
can be challenging. The class gdstk.Curve
can
+be used to facilitate the creation of polygons by drawing their shapes
+step-by-step. The syntax is inspired by the SVG path specification.
# Construct a curve made of a sequence of line segments
+c1 = gdstk.Curve((0, 0)).segment([(1, 0), (2, 1), (2, 2), (0, 2)])
+p1 = gdstk.Polygon(c1.points())
+
+# Construct another curve using relative coordinates
+c2 = gdstk.Curve((3, 1)).segment([(1, 0), (2, 1), (2, 2), (0, 2)], relative=True)
+p2 = gdstk.Polygon(c2.points())
+
void example_curves1(Cell& out_cell) {
+ Vec2 points[] = {{1, 0}, {2, 1}, {2, 2}, {0, 2}};
+
+ // Curve points will be copied to the polygons, so allocating the curve on
+ // the stack is fine.
+ Curve c1 = {};
+ c1.init(Vec2{0, 0}, 0.01);
+ c1.segment({.capacity = 0, .count = COUNT(points), .items = points}, false);
+
+ Polygon* p1 = (Polygon*)allocate_clear(sizeof(Polygon));
+ p1->point_array.extend(c1.point_array);
+ out_cell.polygon_array.append(p1);
+ c1.clear();
+
+ Curve c2 = {};
+ c2.init(Vec2{3, 1}, 0.01);
+ c2.segment({.capacity = 0, .count = COUNT(points), .items = points}, true);
+
+ Polygon* p2 = (Polygon*)allocate_clear(sizeof(Polygon));
+ p2->point_array.extend(c2.point_array);
+ out_cell.polygon_array.append(p2);
+ c2.clear();
+}
+
Coordinate pairs can be given as a complex number: real and imaginary parts are +used as x and y coordinates, respectively. That is useful to define points in +polar coordinates.
+Elliptical arcs have syntax similar to gdstk.ellipse()
, but they allow
+for an extra rotation of the major axis of the ellipse.
# Use complex numbers to facilitate writing polar coordinates
+c3 = gdstk.Curve(2j).segment(4 * numpy.exp(1j * numpy.pi / 6), relative=True)
+# Elliptical arcs have syntax similar to gdstk.ellipse
+c3.arc((4, 2), 0.5 * numpy.pi, -0.5 * numpy.pi)
+p3 = gdstk.Polygon(c3.points())
+
void example_curves2(Cell& out_cell) {
+ Curve c3 = {};
+ c3.init(Vec2{0, 2}, 0.01);
+ c3.segment(4 * cplx_from_angle(M_PI / 6), true);
+ c3.arc(4, 2, M_PI / 2, -M_PI / 2, 0);
+
+ Polygon* p3 = (Polygon*)allocate_clear(sizeof(Polygon));
+ p3->point_array.extend(c3.point_array);
+ out_cell.polygon_array.append(p3);
+ c3.clear();
+}
+
Curves sections can be constructed as cubic, quadratic and general-degree
+Bézier curves. Additionally, a smooth interpolating curve can be calculated
+with the method gdstk.Curve.interpolation()
, which has a number of
+arguments to control the shape of the curve.
# Cubic Bezier curves can be easily created
+c4 = gdstk.Curve((0, 0), tolerance=1e-3)
+c4.cubic([(0, 1), (1, 1), (1, 0)])
+# Smooth continuation:
+c4.cubic_smooth([(1, -1), (1, 0)], relative=True)
+
+# Similarly for quadratic Bezier curves
+c4.quadratic([(0.5, 1), (1, 0)], relative=True)
+c4.quadratic_smooth((1, 0), relative=True)
+
+# Smooth interpolating curve
+c4.interpolation([(4, -1), (3, -2), (2, -1.5), (1, -2), (0, -1), (0, 0)])
+p4 = gdstk.Polygon(c4.points())
+
void example_curves3(Cell& out_cell) {
+ Curve c4 = {};
+ c4.init(Vec2{0, 0}, 1e-3);
+
+ Vec2 points1[] = {{0, 1}, {1, 1}, {1, 0}};
+ c4.cubic({.capacity = 0, .count = COUNT(points1), .items = points1}, false);
+
+ Vec2 points2[] = {{1, -1}, {1, 0}};
+ c4.cubic_smooth({.capacity = 0, .count = COUNT(points2), .items = points2}, true);
+
+ Vec2 points3[] = {{0.5, 1}, {1, 0}};
+ c4.quadratic({.capacity = 0, .count = COUNT(points3), .items = points3}, true);
+
+ c4.quadratic_smooth(Vec2{1, 0}, true);
+
+ Vec2 points4[] = {{4, -1}, {3, -2}, {2, -1.5}, {1, -2}, {0, -1}, {0, 0}};
+ double angles[COUNT(points4) + 1] = {};
+ bool angle_constraints[COUNT(points4) + 1] = {};
+ Vec2 tension[COUNT(points4) + 1] = {{1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}};
+
+ c4.interpolation({.capacity = 0, .count = COUNT(points4), .items = points4}, angles,
+ angle_constraints, tension, 1, 1, false, false);
+
+ // The last point will coincide with the first (this can be checked at
+ // runtime with the `closed` method), so we remove it.
+ if (c4.closed()) {
+ c4.remove(c4.point_array.count - 1);
+ }
+ Polygon* p4 = (Polygon*)allocate_clear(sizeof(Polygon));
+ p4->point_array.extend(c4.point_array);
+ out_cell.polygon_array.append(p4);
+ c4.clear();
+}
+
All polygons can be transformed through gdstk.Polygon.translate()
,
+gdstk.Polygon.rotate()
, gdstk.Polygon.scale()
, and
+gdstk.Polygon.mirror()
. The transformations are applied in-place, i.e.,
+no new polygons are created.
poly = gdstk.rectangle((-2, -2), (2, 2))
+poly.rotate(numpy.pi / 4)
+poly.scale(1, 0.5)
+
void example_transformations(Cell& out_cell) {
+ Polygon* poly = (Polygon*)allocate_clear(sizeof(Polygon));
+ *poly = rectangle(Vec2{-2, -2}, Vec2{2, 2}, 0);
+ poly->rotate(M_PI / 4, Vec2{0, 0});
+ poly->scale(Vec2{1, 0.5}, Vec2{0, 0});
+ out_cell.polygon_array.append(poly);
+}
+
All shapes are tagged with 2 properties: layer and data type (or text type in
+the case of gdstk.Label
). They are always 0 by default, but can be
+any integer in the range from 0 to 255.
These properties have no predefined meaning. It is up to the system using the +file to chose with to do with those tags. For example, in the CMOS fabrication +process, each layer could represent a different lithography level.
+In the example below, a single file stores different fabrication masks in +separate layer and data type configurations. Python dictionaries are used to +simplify the assignment to each polygon.
+# Layer/datatype definitions for each step in the fabrication
+ld = {
+ "full etch": {"layer": 1, "datatype": 3},
+ "partial etch": {"layer": 2, "datatype": 3},
+ "lift-off": {"layer": 0, "datatype": 7},
+}
+
+p1 = gdstk.rectangle((-3, -3), (3, 3), **ld["full etch"])
+p2 = gdstk.rectangle((-5, -3), (-3, 3), **ld["partial etch"])
+p3 = gdstk.rectangle((5, -3), (3, 3), **ld["partial etch"])
+p4 = gdstk.regular_polygon((0, 0), 2, 6, **ld["lift-off"])
+
void example_layerdatatype(Cell& out_cell) {
+ Tag t_full_etch = make_tag(1, 3);
+ Tag t_partial_etch = make_tag(2, 3);
+ Tag t_lift_off = make_tag(0, 7);
+
+ Polygon* poly[4];
+ for (uint64_t i = 0; i < COUNT(poly); i++) poly[i] = (Polygon*)allocate_clear(sizeof(Polygon));
+
+ *poly[0] = rectangle(Vec2{-3, -3}, Vec2{3, 3}, t_full_etch);
+ *poly[1] = rectangle(Vec2{-5, -3}, Vec2{-3, 3}, t_partial_etch);
+ *poly[2] = rectangle(Vec2{5, -3}, Vec2{3, 3}, t_partial_etch);
+ *poly[3] = regular_polygon(Vec2{0, 0}, 2, 6, 0, t_lift_off);
+
+ out_cell.polygon_array.extend({.capacity = 0, .count = COUNT(poly), .items = poly});
+}
+
References are responsible for the hierarchical structure of the layout. +Through references, the cell content can be reused in another cell (without +actually copying the whole geometry). As an example, imagine the we are +designing an electronic circuit that uses hundreds of transistors, all with the +same shape. We can draw the transistor just once and reference it throughout +the circuit, rotating or mirroring each instance as necessary.
+Besides creating single references, it is also possible to create full 2D
+arrays with a single entity, both using gdstk.Reference
. Both uses
+are exemplified below.
# Create a cell with a component that is used repeatedly
+contact = gdstk.Cell("CONTACT")
+contact.add(p1, p2, p3, p4)
+
+# Create a cell with the complete device
+device = gdstk.Cell("DEVICE")
+device.add(cutout)
+# Add 2 references to the component changing size and orientation
+ref1 = gdstk.Reference(contact, (3.5, 1), magnification=0.25)
+ref2 = gdstk.Reference(contact, (1, 3.5), magnification=0.25, rotation=numpy.pi / 2)
+device.add(ref1, ref2)
+
+# The final layout has several repetitions of the complete device
+main = gdstk.Cell("MAIN")
+main.add(gdstk.Reference(device, (0, 0), columns=3, rows=2, spacing=(6, 7)))
+
#include <stdio.h>
+
+#include <gdstk/gdstk.hpp>
+
+using namespace gdstk;
+
+int main(int argc, char* argv[]) {
+ Tag t_full_etch = make_tag(1, 3);
+ Tag t_partial_etch = make_tag(2, 3);
+ Tag t_lift_off = make_tag(0, 7);
+
+ char lib_name[] = "library";
+ Library lib = {.name = lib_name, .unit = 1e-6, .precision = 1e-9};
+
+ // CONTACT
+
+ char contact_cell_name[] = "CONTACT";
+ Cell contact_cell = {.name = contact_cell_name};
+ lib.cell_array.append(&contact_cell);
+
+ Polygon contact_poly[4];
+ contact_poly[0] = rectangle(Vec2{-3, -3}, Vec2{3, 3}, t_full_etch);
+ contact_poly[1] = rectangle(Vec2{-5, -3}, Vec2{-3, 3}, t_partial_etch);
+ contact_poly[2] = rectangle(Vec2{5, -3}, Vec2{3, 3}, t_partial_etch);
+ contact_poly[3] = regular_polygon(Vec2{0, 0}, 2, 6, 0, t_lift_off);
+
+ Polygon* p[] = {contact_poly, contact_poly + 1, contact_poly + 2, contact_poly + 3};
+ contact_cell.polygon_array.extend({.capacity = 0, .count = COUNT(p), .items = p});
+
+ // DEVICE
+
+ char device_cell_name[] = "DEVICE";
+ Cell device_cell = {.name = device_cell_name};
+ lib.cell_array.append(&device_cell);
+
+ Vec2 cutout_points[] = {{0, 0}, {5, 0}, {5, 5}, {0, 5}, {0, 0},
+ {2, 2}, {2, 3}, {3, 3}, {3, 2}, {2, 2}};
+ Polygon cutout_poly = {};
+ cutout_poly.point_array.extend(
+ {.capacity = 0, .count = COUNT(cutout_points), .items = cutout_points});
+ device_cell.polygon_array.append(&cutout_poly);
+
+ Reference contact_ref1 = {
+ .type = ReferenceType::Cell,
+ .cell = &contact_cell,
+ .origin = Vec2{3.5, 1},
+ .magnification = 0.25,
+ };
+ device_cell.reference_array.append(&contact_ref1);
+
+ Reference contact_ref2 = {
+ .type = ReferenceType::Cell,
+ .cell = &contact_cell,
+ .origin = Vec2{1, 3.5},
+ .rotation = M_PI / 2,
+ .magnification = 0.25,
+ };
+ device_cell.reference_array.append(&contact_ref2);
+
+ // MAIN
+
+ char main_cell_name[] = "MAIN";
+ Cell main_cell = {.name = main_cell_name};
+ lib.cell_array.append(&main_cell);
+
+ Reference device_ref = {
+ .type = ReferenceType::Cell,
+ .cell = &device_cell,
+ .magnification = 1,
+ .repetition = {RepetitionType::Rectangular, 3, 2, Vec2{6, 7}},
+ };
+ main_cell.reference_array.append(&device_ref);
+
+ // Output
+
+ lib.write_gds("references.gds", 0, NULL);
+
+ for (uint64_t i = 0; i < COUNT(contact_poly); i++) contact_poly[i].clear();
+ cutout_poly.clear();
+ contact_cell.polygon_array.clear();
+ device_cell.polygon_array.clear();
+ device_cell.reference_array.clear();
+ main_cell.reference_array.clear();
+ lib.cell_array.clear();
+ return 0;
+}
+
Besides polygons, the GDSII and OASIS formats define paths, witch are +polygonal chains with +associated width and end caps. The width is a single number, constant +throughout the path, and the end caps can be flush, round (GDSII only), or +extended by a custom distance.
+There is no specification for the joins between adjacent segments, so it is up +to the system using the file to specify those. Usually the joins are straight +extensions of the path boundaries up to some beveling limit. Gdstk also uses +this specification for the joins.
+It is possible to circumvent all of the above limitations within Gdstk by +storing paths as polygons in the GDSII or OASIS file. The disadvantage of this +solution is that other software will not be able to edit the geometry as paths, +since that information is lost.
+The construction of paths (either GDSII/OASIS paths or polygonal paths) in
+Gdstk is based on gdstk.FlexPath
and gdstk.RobustPath
.
The class gdstk.FlexPath
is a mirror of gdstk.Curve
before,
+with additional features to facilitate path creation:
all curve construction methods are available;
path width can be easily controlled throughout the path;
end caps and joins can be specified by the user;
straight segments can be automatically joined by circular arcs;
multiple parallel paths can be designed simultaneously;
spacing between parallel paths is arbitrary - the user specifies the offset +of each path individually.
# Path defined by a sequence of points and stored as a GDSII path
+fp1 = gdstk.FlexPath(
+ [(0, 0), (3, 0), (3, 2), (5, 3), (3, 4), (0, 4)], 1, simple_path=True
+)
+
+# Other construction methods can still be used
+fp1.interpolation([(0, 2), (2, 2), (4, 3), (5, 1)], relative=True)
+
+# Multiple parallel paths separated by 0.5 with different widths,
+# end caps, and joins. Because of the join specification, they
+# cannot be stared as GDSII paths, only as polygons.
+fp2 = gdstk.FlexPath(
+ [(12, 0), (8, 0), (8, 3), (10, 2)],
+ [0.3, 0.2, 0.4],
+ 0.5,
+ ends=["extended", "flush", "round"],
+ joins=["bevel", "miter", "round"],
+)
+fp2.arc(2, -0.5 * numpy.pi, 0.5 * numpy.pi)
+fp2.arc(1, 0.5 * numpy.pi, 1.5 * numpy.pi)
+
Cell* example_flexpath1(const char* name) {
+ Cell* out_cell = (Cell*)allocate_clear(sizeof(Cell));
+ out_cell->name = copy_string(name, NULL);
+
+ FlexPath* fp = (FlexPath*)allocate_clear(sizeof(FlexPath));
+ fp->init(Vec2{0, 0}, 1, 0.5, 0, 0.01, 0);
+ fp->simple_path = true;
+
+ Vec2 points1[] = {{3, 0}, {3, 2}, {5, 3}, {3, 4}, {0, 4}};
+ fp->segment({.capacity = 0, .count = COUNT(points1), .items = points1}, NULL, NULL, false);
+
+ out_cell->flexpath_array.append(fp);
+
+ fp = (FlexPath*)allocate_clear(sizeof(FlexPath));
+ const double widths[] = {0.3, 0.2, 0.4};
+ const double offsets[] = {-0.5, 0, 0.5};
+ const Tag tags[] = {0, 0, 0};
+ fp->init(Vec2{12, 0}, 3, widths, offsets, 0.01, tags);
+
+ fp->elements[0].end_type = EndType::HalfWidth;
+ fp->elements[0].join_type = JoinType::Bevel;
+
+ fp->elements[1].end_type = EndType::Flush;
+ fp->elements[1].join_type = JoinType::Miter;
+
+ fp->elements[2].end_type = EndType::Round;
+ fp->elements[2].join_type = JoinType::Round;
+
+ Vec2 points2[] = {{8, 0}, {8, 3}, {10, 2}};
+ fp->segment({.capacity = 0, .count = COUNT(points2), .items = points2}, NULL, NULL, false);
+
+ fp->arc(2, 2, -M_PI / 2, M_PI / 2, 0, NULL, NULL);
+ fp->arc(1, 1, M_PI / 2, 1.5 * M_PI, 0, NULL, NULL);
+
+ out_cell->flexpath_array.append(fp);
+ return out_cell;
+}
+
The corner type “circular bend” (together with the bend_radius argument) can +be used to automatically curve the path.
+# Path created with automatic bends of radius 5
+points = [(0, 0), (0, 10), (20, 0), (18, 15), (8, 15)]
+fp3 = gdstk.FlexPath(points, 0.5, bend_radius=5, simple_path=True)
+
+# Same path, generated with natural joins, for comparison
+fp4 = gdstk.FlexPath(points, 0.5, layer=1, simple_path=True)
+
Cell* example_flexpath2(const char* name) {
+ Cell* out_cell = (Cell*)allocate_clear(sizeof(Cell));
+ out_cell->name = copy_string(name, NULL);
+
+ Vec2 points[] = {{0, 10}, {20, 0}, {18, 15}, {8, 15}};
+
+ for (uint64_t i = 0; i < 2; i++) {
+ FlexPath* fp = (FlexPath*)allocate_clear(sizeof(FlexPath));
+ fp->init(Vec2{0, 0}, 1, 0.5, 0, 0.01, 0);
+ fp->simple_path = true;
+ if (i == 0) {
+ fp->elements[0].bend_type = BendType::Circular;
+ fp->elements[0].bend_radius = 5;
+ } else {
+ fp->elements[0].tag = make_tag(1, 0);
+ }
+ fp->segment({.capacity = 0, .count = COUNT(points), .items = points}, NULL, NULL, false);
+ out_cell->flexpath_array.append(fp);
+ }
+
+ return out_cell;
+}
+
Width and offset variations are possible throughout the path. Changes are +linearly tapered in the path section they are defined. Note that, because +width changes are not possible for GDSII/OASIS paths, they will be stored as +polygonal objects.
+# Straight segment showing the possibility of width and offset changes
+fp5 = gdstk.FlexPath((0, 0), [0.5, 0.5], 1)
+fp5.horizontal(2)
+fp5.horizontal(4, width=0.8, offset=1.8)
+fp5.horizontal(6)
+
Cell* example_flexpath3(const char* name) {
+ Cell* out_cell = (Cell*)allocate_clear(sizeof(Cell));
+ out_cell->name = copy_string(name, NULL);
+
+ double widths[] = {0.5, 0.5};
+ double offsets[] = {-0.5, 0.5};
+ Tag tags[] = {0, 0};
+ FlexPath* fp = (FlexPath*)allocate_clear(sizeof(FlexPath));
+ fp->init(Vec2{0, 0}, 2, widths, offsets, 0.01, tags);
+
+ fp->horizontal(2, NULL, NULL, false);
+
+ widths[0] = 0.8;
+ widths[1] = 0.8;
+ offsets[0] = -0.9;
+ offsets[1] = 0.9;
+ fp->horizontal(4, widths, offsets, false);
+
+ fp->horizontal(6, NULL, NULL, false);
+
+ out_cell->flexpath_array.append(fp);
+
+ return out_cell;
+}
+
In some situations, gdstk.FlexPath
is unable to properly calculate all
+the joins. This often happens when the width or offset of the path is
+relatively large with respect to the length of the segments being joined.
+Curves that meet other curves or segments at sharp angles are a typical example
+where this often happens.
The class gdstk.RobustPath
can be used in such scenarios where curved
+sections are expected to meet at sharp angles. The drawbacks of using
+gdstk.RobustPath
are the extra computational resources required to
+calculate all joins and the impossibility of specifying joins. The advantages
+are, as mentioned earlier, more robustness when generating the final geometry,
+and freedom to use custom functions to parameterize the widths or offsets of
+the paths.
# Create 4 parallel paths in different layers
+rp = gdstk.RobustPath(
+ (0, 50),
+ [2, 0.5, 1, 1],
+ [0, 0, -1, 1],
+ ends=["extended", "round", "flush", "flush"],
+ layer=[1, 0, 2, 2],
+)
+rp.segment((0, 45))
+rp.segment(
+ (0, 5),
+ width=[lambda u: 2 + 16 * u * (1 - u), 0.5, 1, 1],
+ offset=[
+ 0,
+ lambda u: 8 * u * (1 - u) * numpy.cos(12 * numpy.pi * u),
+ lambda u: -1 - 8 * u * (1 - u),
+ lambda u: 1 + 8 * u * (1 - u),
+ ],
+)
+rp.segment((0, 0))
+rp.interpolation(
+ [(15, 5)],
+ angles=[0, 0.5 * numpy.pi],
+ width=0.5,
+ offset=[-0.25, 0.25, -0.75, 0.75],
+)
+rp.parametric(
+ lambda u: numpy.array((4 * numpy.sin(6 * numpy.pi * u), 45 * u)),
+ offset=[
+ lambda u: -0.25 * numpy.cos(24 * numpy.pi * u),
+ lambda u: 0.25 * numpy.cos(24 * numpy.pi * u),
+ -0.75,
+ 0.75,
+ ],
+)
+
#include <stdio.h>
+
+#include <gdstk/gdstk.hpp>
+
+using namespace gdstk;
+
+double parametric1(double u, void*) { return 2 + 16 * u * (1 - u); }
+
+double parametric2(double u, void*) { return 8 * u * (1 - u) * cos(12 * M_PI * u); }
+
+double parametric3(double u, void*) { return -1 - 8 * u * (1 - u); }
+
+double parametric4(double u, void*) { return 1 + 8 * u * (1 - u); }
+
+double parametric5(double u, void*) { return -0.25 * cos(24 * M_PI * u); }
+
+double parametric6(double u, void*) { return 0.25 * cos(24 * M_PI * u); }
+
+Vec2 parametric7(double u, void*) { return Vec2{4 * sin(6 * M_PI * u), 45 * u}; }
+
+int main(int argc, char* argv[]) {
+ double widths[] = {2, 0.5, 1, 1};
+ double offsets[] = {0, 0, -1, 1};
+ Tag tags[] = {make_tag(1, 0), make_tag(0, 0), make_tag(2, 0), make_tag(2, 0)};
+ RobustPath rp = {};
+ rp.init(Vec2{0, 50}, 4, widths, offsets, 0.01, 1000, tags);
+ rp.scale_width = true;
+ rp.elements[0].end_type = EndType::HalfWidth;
+ rp.elements[1].end_type = EndType::Round;
+ rp.elements[2].end_type = EndType::Flush;
+ rp.elements[3].end_type = EndType::Flush;
+
+ rp.segment(Vec2{0, 45}, NULL, NULL, false);
+
+ Interpolation width1[] = {
+ {.type = InterpolationType::Parametric},
+ {.type = InterpolationType::Constant},
+ {.type = InterpolationType::Constant},
+ {.type = InterpolationType::Constant},
+ };
+ width1[0].function = parametric1;
+ for (int i = 1; i < COUNT(width1); i++) {
+ width1[i].value = rp.elements[i].end_width;
+ }
+ Interpolation offset1[] = {
+ {.type = InterpolationType::Constant},
+ {.type = InterpolationType::Parametric},
+ {.type = InterpolationType::Parametric},
+ {.type = InterpolationType::Parametric},
+ };
+ offset1[0].value = 0;
+ offset1[1].function = parametric2;
+ offset1[2].function = parametric3;
+ offset1[3].function = parametric4;
+ rp.segment(Vec2{0, 5}, width1, offset1, false);
+
+ rp.segment(Vec2{0, 0}, NULL, NULL, false);
+
+ Vec2 point = {15, 5};
+ double angles[] = {0, M_PI / 2};
+ bool angle_constraints[] = {true, true};
+ Vec2 tension[] = {{1, 1}, {1, 1}};
+ Interpolation width2[] = {
+ {.type = InterpolationType::Linear},
+ {.type = InterpolationType::Linear},
+ {.type = InterpolationType::Linear},
+ {.type = InterpolationType::Linear},
+ };
+ Interpolation offset2[] = {
+ {.type = InterpolationType::Linear},
+ {.type = InterpolationType::Linear},
+ {.type = InterpolationType::Linear},
+ {.type = InterpolationType::Linear},
+ };
+ for (int i = 0; i < COUNT(width2); i++) {
+ width2[i].initial_value = rp.elements[i].end_width;
+ width2[i].final_value = 0.5;
+ offset2[i].initial_value = rp.elements[i].end_offset;
+ }
+ offset2[0].final_value = -0.25;
+ offset2[1].final_value = 0.25;
+ offset2[2].final_value = -0.75;
+ offset2[3].final_value = 0.75;
+ rp.interpolation({.capacity = 0, .count = 1, .items = &point}, angles, angle_constraints,
+ tension, 1, 1, false, width2, offset2, false);
+
+ Interpolation offset3[] = {
+ {.type = InterpolationType::Parametric},
+ {.type = InterpolationType::Parametric},
+ {.type = InterpolationType::Constant},
+ {.type = InterpolationType::Constant},
+ };
+ offset3[0].function = parametric5;
+ offset3[1].function = parametric6;
+ offset3[2].value = -0.75;
+ offset3[3].value = 0.75;
+ rp.parametric(parametric7, NULL, NULL, NULL, NULL, offset3, true);
+
+ char robustpath_cell_name[] = "RobustPath";
+ Cell robustpath_cell = {.name = robustpath_cell_name};
+ robustpath_cell.robustpath_array.append(&rp);
+
+ char lib_name[] = "Paths";
+ Library lib = {.name = lib_name, .unit = 1e-6, .precision = 1e-9};
+ lib.cell_array.append(&robustpath_cell);
+
+ lib.write_gds("robustpaths.gds", 0, NULL);
+
+ rp.clear();
+ robustpath_cell.robustpath_array.clear();
+ lib.cell_array.clear();
+ return 0;
+}
+
Note that, analogously to gdstk.FlexPath
, gdstk.RobustPath
+can be stored as a GDSII/OASIS path as long as its width is kept constant.
In the context of a GDSII/OASIS file, text is supported in the form of labels,
+which are ASCII annotations placed somewhere in the geometry of a given cell.
+Similar to polygons, labels are tagged with layer and text type values (text
+type is the label equivalent of the polygon data type). They are supported by
+the class gdstk.Label
.
Additionally, Gdstk offers the possibility of creating text as polygons to be
+included with the geometry. The function gdstk.text()
creates polygonal
+text that can be used in the same way as any other polygons in Gdstk. The font
+used to render the characters contains only horizontal and vertical edges,
+which is important for some laser writing systems.
# Label centered at (1, 3)
+label = gdstk.Label("Sample label", (5, 3), texttype=2)
+
+# Horizontal text with height 2.25
+htext = gdstk.text("12345", 2.25, (0.25, 6))
+
+# Vertical text with height 1.5
+vtext = gdstk.text("ABC", 1.5, (10.5, 4), vertical=True)
+
+rect = gdstk.rectangle((0, 0), (10, 6), layer=10)
+
#include <stdio.h>
+
+#include <gdstk/gdstk.hpp>
+
+using namespace gdstk;
+
+int main(int argc, char* argv[]) {
+ char lib_name[] = "Text";
+ Library lib = {.name = lib_name, .unit = 1e-6, .precision = 1e-9};
+
+ char text_cell_name[] = "Text";
+ Cell text_cell = {.name = text_cell_name};
+ lib.cell_array.append(&text_cell);
+
+ char label_text[] = "Sample label";
+ Label label = {
+ .tag = make_tag(0, 2),
+ .text = label_text,
+ .origin = Vec2{5, 3},
+ .magnification = 1,
+ };
+ text_cell.label_array.append(&label);
+
+ Array<Polygon*> all_text = {};
+ text("12345", 2.25, Vec2{0.25, 6}, false, 0, all_text);
+ text("ABC", 1.5, Vec2{10.5, 4}, true, 0, all_text);
+ text_cell.polygon_array.extend(all_text);
+
+ Polygon rect = rectangle(Vec2{0, 0}, Vec2{10, 6}, make_tag(10, 0));
+ text_cell.polygon_array.append(&rect);
+
+ lib.write_gds("text.gds", 0, NULL);
+
+ for (uint64_t i = 0; i < all_text.count; i++) {
+ all_text[i]->clear();
+ free_allocation(all_text[i]);
+ }
+ all_text.clear();
+ rect.clear();
+ text_cell.label_array.clear();
+ text_cell.polygon_array.clear();
+ lib.cell_array.clear();
+ return 0;
+}
+
Gdstk offers a number of functions and methods to modify existing geometry.
+The most useful operations include gdstk.boolean()
, gdstk.slice()
,
+gdstk.offset()
, and gdstk.Polygon.fillet()
.
Boolean operations (gdstk.boolean()
) can be performed on polygons, paths
+and whole cells. Four operations are defined: union (“or”), intersection
+(“and”), subtraction (“not”), and symmetric difference (“xor”). They can be
+computationally expensive, so it is usually advisable to avoid using boolean
+operations whenever possible. If they are necessary, keeping the number of
+vertices is all polygons as low as possible also helps.
# Create some text
+text = gdstk.text("GDSTK", 4, (0, 0))
+# Create a rectangle extending the text's bounding box by 1
+rect = gdstk.rectangle((-1, -1), (5 * 4 * 9 / 16 + 1, 4 + 1))
+
+# Subtract the text from the rectangle
+inv = gdstk.boolean(rect, text, "not")
+
Cell* example_boolean(const char* name) {
+ Cell* out_cell = (Cell*)allocate_clear(sizeof(Cell));
+ out_cell->name = copy_string(name, NULL);
+
+ Array<Polygon*> txt = {};
+ text("GDSTK", 4, Vec2{0, 0}, false, 0, txt);
+
+ Polygon rect = rectangle(Vec2{-1, -1}, Vec2{5 * 4 * 9 / 16 + 1, 4 + 1}, 0);
+
+ boolean(rect, txt, Operation::Not, 1000, out_cell->polygon_array);
+
+ for (int i = 0; i < txt.count; i++) {
+ txt[i]->clear();
+ free_allocation(txt[i]);
+ }
+ txt.clear();
+ rect.clear();
+
+ return out_cell;
+}
+
As the name indicates, a slice operation subdivides a set of polygons along +horizontal or vertical cut lines.
+ring1 = gdstk.ellipse((-6, 0), 6, inner_radius=4)
+ring2 = gdstk.ellipse((0, 0), 6, inner_radius=4)
+ring3 = gdstk.ellipse((6, 0), 6, inner_radius=4)
+
+# Slice the first ring across x=-3, the second ring across x=-3
+# and x=3, and the third ring across x=3
+slices1 = gdstk.slice(ring1, -3, "x")
+slices2 = gdstk.slice(ring2, [-3, 3], "x")
+slices3 = gdstk.slice(ring3, 3, "x")
+
+slices = gdstk.Cell("SLICES")
+
+# Keep only the left side of slices1, the center part of slices2
+# and the right side of slices3
+slices.add(*slices1[0])
+slices.add(*slices2[1])
+slices.add(*slices3[1])
+
Cell* example_slice(const char* name) {
+ Cell* out_cell = (Cell*)allocate_clear(sizeof(Cell));
+ out_cell->name = copy_string(name, NULL);
+
+ Polygon ring[3];
+ ring[0] = ellipse(Vec2{-6, 0}, 6, 6, 4, 4, 0, 0, 0.01, 0);
+ ring[1] = ellipse(Vec2{0, 0}, 6, 6, 4, 4, 0, 0, 0.01, 0);
+ ring[2] = ellipse(Vec2{6, 0}, 6, 6, 4, 4, 0, 0, 0.01, 0);
+
+ double x[] = {-3, 3};
+ Array<double> cuts = {.capacity = 0, .count = 1, .items = x};
+ Array<Polygon*> result[3] = {};
+ slice(ring[0], cuts, true, 1000, result);
+ out_cell->polygon_array.extend(result[0]);
+ for (uint64_t i = 0; i < result[1].count; i++) {
+ result[1][i]->clear();
+ free_allocation(result[1][i]);
+ }
+ result[0].clear();
+ result[1].clear();
+
+ cuts.count = 2;
+ slice(ring[1], cuts, true, 1000, result);
+ out_cell->polygon_array.extend(result[1]);
+ for (uint64_t i = 0; i < result[0].count; i++) {
+ result[0][i]->clear();
+ free_allocation(result[0][i]);
+ }
+ for (uint64_t i = 0; i < result[2].count; i++) {
+ result[2][i]->clear();
+ free_allocation(result[2][i]);
+ }
+ result[0].clear();
+ result[1].clear();
+ result[2].clear();
+
+ cuts.count = 1;
+ cuts.items = x + 1;
+ slice(ring[2], cuts, true, 1000, result);
+ out_cell->polygon_array.extend(result[1]);
+ for (uint64_t i = 0; i < result[0].count; i++) {
+ result[0][i]->clear();
+ free_allocation(result[0][i]);
+ }
+ result[0].clear();
+ result[1].clear();
+
+ ring[0].clear();
+ ring[1].clear();
+ ring[2].clear();
+
+ return out_cell;
+}
+
The function gdstk.offset()
dilates or erodes polygons by a fixed amount.
+It can operate on individual polygons or sets of them, in which case it may be
+necessary to set use_union = True
to remove the impact of inner edges. The
+same is valid for polygons with holes.
rect1 = gdstk.rectangle((-4, -4), (1, 1))
+rect2 = gdstk.rectangle((-1, -1), (4, 4))
+
+# Erosion: because we set `use_union=True`, the inner boundaries have no effect
+outer = gdstk.offset([rect1, rect2], -0.5, use_union=True, layer=1)
+
Cell* example_offset(const char* name) {
+ Cell* out_cell = (Cell*)allocate_clear(sizeof(Cell));
+ out_cell->name = copy_string(name, NULL);
+
+ Polygon* rect = (Polygon*)allocate(sizeof(Polygon));
+ *rect = rectangle(Vec2{-4, -4}, Vec2{1, 1}, 0);
+ out_cell->polygon_array.append(rect);
+
+ rect = (Polygon*)allocate(sizeof(Polygon));
+ *rect = rectangle(Vec2{-1, -1}, Vec2{4, 4}, 0);
+ out_cell->polygon_array.append(rect);
+
+ uint64_t start = out_cell->polygon_array.count;
+ offset(out_cell->polygon_array, -0.5, OffsetJoin::Miter, 2, 1000, true,
+ out_cell->polygon_array);
+ for (uint64_t i = start; i < out_cell->polygon_array.count; i++) {
+ out_cell->polygon_array[i]->tag = make_tag(1, 0);
+ }
+
+ return out_cell;
+}
+
The method gdstk.Polygon.fillet()
can be used to round polygon corners.
flexpath = gdstk.FlexPath([(-8, -4), (0, -4), (0, 4), (8, 4)], 4)
+filleted_path = flexpath.to_polygons()[0]
+filleted_path.fillet(1.5)
+
Cell* example_fillet(const char* name) {
+ Cell* out_cell = (Cell*)allocate_clear(sizeof(Cell));
+ out_cell->name = copy_string(name, NULL);
+
+ FlexPath flexpath = {};
+ flexpath.init(Vec2{-8, -4}, 1, 4, 0, 0.01, 0);
+ Vec2 points[] = {{0, -4}, {0, 4}, {8, 4}};
+ flexpath.segment({.capacity = 0, .count = COUNT(points), .items = points}, NULL, NULL, false);
+
+ Array<Polygon*> poly_array = {};
+ flexpath.to_polygons(false, 0, poly_array);
+ flexpath.clear();
+
+ double r = 1.5;
+ for (int i = 0; i < poly_array.count; i++)
+ poly_array[i]->fillet({.capacity = 0, .count = 1, .items = &r}, 0.01);
+
+ out_cell->polygon_array.extend(poly_array);
+ poly_array.clear();
+
+ return out_cell;
+}
+
All the information used to create a GDSII/OASIS file is kept within an
+instance of gdstk.Library
. Besides all the geometric and hierarchical
+information, this class also holds a name and the units for all entities. The
+name can be any ASCII string — it is simply stored in the file and has no other
+purpose in Gdstk. The units require some attention because they can impact the
+resolution of the polygons in the library when written to a file.
Two values are defined when creating a gdstk.Library
: unit
and
+precision
. The value of unit
defines the unit size—in meters—for all
+entities in the library. For example, if unit = 1e-6
(10⁻⁶ m, the default
+value), a vertex at (1, 2) should be interpreted as a vertex in real world
+position (1 × 10⁻⁶ m, 2 × 10⁻⁶ m). If unit
changes to 0.001, then that
+same vertex would be located (in real world coordinates) at (0.001 m, 0.002 m),
+or (1 mm, 2 mm).
The value of precision has to do with the type used to store coordinates in the
+GDSII file: signed 4-byte integers. Because of that, a finer coordinate grid
+than 1 unit
is usually desired to define coordinates. That grid is
+defined, in meters, by precision
, which defaults to 1e-9
(10⁻⁹ m).
+When the GDSII file is written, all vertices are snapped to the grid defined by
+precision
. For example, for the default values of unit
and
+precision
, a vertex at (1.0512, 0.0001) represents real world coordinates
+(1.0512 × 10⁻⁶ m, 0.0001 × 10⁻⁶ m), or (1051.2 × 10⁻⁹ m, 0.1 × 10⁻⁹ m), which
+will be rounded to integers: (1051 × 10⁻⁹ m, 0 × 10⁻⁹ m), or (1.051 × 10⁻⁶ m, 0
+× 10⁻⁶ m). The actual coordinate values written in the GDSII file will be the
+integers (1051, 0). By reducing the value of precision
from 10⁻⁹ m to
+10⁻¹² m, for example, the coordinates will have 3 additional decimal places of
+precision, so the stored values would be (1051200, 100).
The downside of increasing the number of decimal places in the file is reducing
+the range of coordinates that can be stored (in real world units). That is
+because the range of coordinate values that can be written in the file are
+[-(2³²); 2³¹ - 1] = [-2,147,483,648; 2,147,483,647]. For the default
+precsision
, this range is [-2.147483648 m; 2.147483647 m]. If
+precision
is set to 10⁻¹² m, the same range is reduced by 1000 times:
+[-2.147483648 mm; 2.147483647 mm].
GDSII files keep a record of both unit
and precision
, which means that
+some care must be taken when mixing geometry from different files to ensure
+they have the same unit
. For that reason, the use of the industry standard
+(unit = 1e-6
) is recommended. OASIS files always use this standard for
+units, whereas precision
can be freely chosen. For that reason, when
+saving or loading an OASIS file with Gdstk, the units can be automatic
+converted.
To save a GDSII file, simply use the gdstk.Library.write_gds()
method, as
+in the First Layout. An OASIS file can be similarly created with
+gdstk.Library.write_oas()
.
An SVG image from a specific cell can also be exported through
+gdstk.Cell.write_svg()
, which was also demonstrated in
+First Layout.
The functions gdstk.read_gds()
and gdstk.read_oas()
load an
+existing GDSII or OASIS file into a new instance of gdstk.Library
.
# Load a GDSII file into a new library.
+lib1 = gdstk.read_gds("filename.gds")
+# Verify the unit used in the library.
+print(lib1.unit)
+
+# Load the same file, but convert all units to nm.
+lib2 = gdstk.read_gds("filename.gds", 1e-9)
+
+# Load an OASIS file into a third library.
+# The library will use unit=1e-6, the default for OASIS.
+lib3 = gdstk.read_oas("filename.oas")
+
// Use units from infile
+Library lib1 = read_gds("filename.gds", 0);
+
+// Convert to new unit
+Library lib2 = read_gds("filename.gds", 1e-9);
+
+// Use default OASIS unit (1e-6)
+Library lib3 = read_oas("filename.oas");
+
Access to the cells in the loaded library is primarily provided through the
+list gdstk.Library.cells
. As a shorthand, if the desired cell name is
+know, the idiom lib1["CELL_NAME"]
can be used, although it is not as
+efficient as building a cell dictionary if a large number of queries is
+expected.
Additionally, the method gdstk.Library.top_level()
can be used to find
+the top-level cells in the library (cells on the top of the hierarchy, i.e.,
+cell that are not referenced by any other cells).
Library loaded using the previous method have all their elements interpreted
+and re-created by Gdstk. This can be time-consuming for large layouts. If the
+reason for loading a file is simply to re-use it’s cells without any
+modifications, the function gdstk.read_rawcells()
is much more efficient.
# Load all cells from a GDSII file without creating the actual geometry
+cells = gdstk.read_rawcells("filename.gds")
+
+# Use some loaded cell in the current design
+my_ref = gdstk.Reference(cells["SOME_CELL"], (0, 0))
+
Map<RawCell*> cells = read_rawcells("filename.gds");
+
+Reference my_ref = {};
+my_ref.init(cells.get("SOME_CELL"), 1);
+
Note
+This method only works when using the GDSII format; OASIS does not support
+gdstk.RawCell
. Units are not changed in this process, so the
+current design must use the same unit
and precision
as the loaded
+cells.
#ifdef GDSTK_CUSTOM_ALLOCATOR
+
+void* allocate(uint64_t size);
+
+void* reallocate(void* ptr, uint64_t size);
+
+void* allocate_clear(uint64_t size);
+
+void free_allocation(void* ptr);
+
+#else // GDSTK_CUSTOM_ALLOCATOR
+
+inline void* allocate(uint64_t size) { return malloc(size); };
+
+inline void* reallocate(void* ptr, uint64_t size) { return realloc(ptr, size); };
+
+inline void* allocate_clear(uint64_t size) { return calloc(1, size); };
+
+inline void free_allocation(void* ptr) { free(ptr); };
+
+#endif // GDSTK_CUSTOM_ALLOCATOR
+
template <class T>
+struct Array {
+ uint64_t capacity; // allocated capacity
+ uint64_t count; // number of slots used
+ T* items; // slots
+
+ T& operator[](uint64_t idx) { return items[idx]; }
+ const T& operator[](uint64_t idx) const { return items[idx]; }
+
+ void print(bool all) const {
+ printf("Array <%p>, count %" PRIu64 "/%" PRIu64 "\n", this, count, capacity);
+ if (all && count > 0) {
+ printf("<%p>", (void*)items[0]);
+ for (uint64_t i = 0; i < count; ++i) {
+ printf(" <%p>", (void*)items[i]);
+ }
+ putchar('\n');
+ }
+ }
+
+ void clear() {
+ if (items) free_allocation(items);
+ items = NULL;
+ capacity = 0;
+ count = 0;
+ }
+
+ bool contains(const T& item) const { return index(item) < count; }
+
+ // Return the index of an array item. If the item is not found, return the
+ // array count.
+ uint64_t index(const T& item) const {
+ T* it = items;
+ for (uint64_t j = 0; j < count; j++)
+ if (*(it++) == item) return j;
+ return count;
+ }
+
+ void append(T item) {
+ if (count == capacity) {
+ capacity = capacity >= INITIAL_ARRAY_CAPACITY ? capacity * ARRAY_GROWTH_FACTOR
+ : INITIAL_ARRAY_CAPACITY;
+ items = (T*)reallocate(items, sizeof(T) * capacity);
+ }
+ items[count++] = item;
+ }
+
+ // Does NOT check capacity. To be used after ensure_slots, for example.
+ void append_unsafe(T item) { items[count++] = item; }
+
+ // Insert item at specified index, pushing the remaining forward
+ void insert(uint64_t index_, T item) {
+ if (count == capacity) {
+ capacity = capacity >= INITIAL_ARRAY_CAPACITY ? capacity * ARRAY_GROWTH_FACTOR
+ : INITIAL_ARRAY_CAPACITY;
+ items = (T*)reallocate(items, sizeof(T) * capacity);
+ }
+ if (index_ >= count) {
+ append_unsafe(item);
+ } else {
+ memmove(items + index_ + 1, items + index_, sizeof(T) * (count - index_));
+ items[index_] = item;
+ count++;
+ }
+ }
+
+ // Remove the item at index by substituting it with the last item in the
+ // array.
+ void remove_unordered(uint64_t index_) { items[index_] = items[--count]; }
+
+ // Remove the item at index and pull the remaining to fill the gap.
+ void remove(uint64_t index_) {
+ memmove(items + index_, items + index_ + 1, sizeof(T) * ((--count) - index_));
+ }
+
+ // Remove (ordered) the first occurrence of a specific item in the array.
+ // Return false if the item cannot be found.
+ bool remove_item(const T& item) {
+ uint64_t i = index(item);
+ if (i == count) return false;
+ remove(i);
+ return true;
+ }
+
+ // Ensure at least the specified number of free slots at the end
+ void ensure_slots(uint64_t free_slots) {
+ if (capacity < count + free_slots) {
+ capacity = count + free_slots;
+ items = (T*)reallocate(items, sizeof(T) * capacity);
+ }
+ }
+
+ // Extend the array by appending all elements from src (in order)
+ void extend(const Array<T>& src) {
+ ensure_slots(src.count);
+ memcpy(items + count, src.items, sizeof(T) * src.count);
+ count += src.count;
+ }
+
+ // The instance should be zeroed before copy_from
+ void copy_from(const Array<T>& src) {
+ capacity = src.count;
+ count = src.count;
+ if (count > 0) {
+ items = (T*)allocate(sizeof(T) * capacity);
+ memcpy(items, src.items, sizeof(T) * count);
+ } else {
+ items = NULL;
+ }
+ }
+};
+
+template <>
+inline void Array<Vec2>::print(bool all) const {
+ printf("Array <%p>, count %" PRIu64 "/%" PRIu64 "\n", this, count, capacity);
+ if (all && count > 0) {
+ printf("(%lg, %lg)", items[0].x, items[0].y);
+ for (uint64_t i = 1; i < count; ++i) {
+ printf(" (%lg, %lg)", items[i].x, items[i].y);
+ }
+ putchar('\n');
+ }
+}
+
+template <>
+inline void Array<IntVec2>::print(bool all) const {
+ printf("Array <%p>, count %" PRIu64 "/%" PRIu64 "\n", this, count, capacity);
+ if (all && count > 0) {
+ printf(" (%" PRId64 ", %" PRId64 ")", items[0].x, items[0].y);
+ for (uint64_t i = 1; i < count; ++i) {
+ printf(" (%" PRId64 ", %" PRId64 ")", items[i].x, items[i].y);
+ }
+ putchar('\n');
+ }
+}
+
+template <>
+inline void Array<double>::print(bool all) const {
+ printf("Array <%p>, count %" PRIu64 "/%" PRIu64 "\n", this, count, capacity);
+ if (all && count > 0) {
+ printf(" %lg", items[0]);
+ for (uint64_t i = 1; i < count; ++i) {
+ printf(" %lg", items[i]);
+ }
+ putchar('\n');
+ }
+}
+
// Must return true if the first argument is ordered before (is less than) the
+// second argument.
+typedef bool (*PolygonComparisonFunction)(Polygon* const&, Polygon* const&);
+
+// This structure is used for caching bounding box and convex hull results from
+// cells. This is a snapshot of the cells at a specific point in time. It
+// must be invalidated whenever the cell contents changes.
+struct GeometryInfo {
+ Array<Vec2> convex_hull;
+ Vec2 bounding_box_min;
+ Vec2 bounding_box_max;
+
+ // These flags indicate whether the convex hull and bounding box values are
+ // valid, even when convex_hull.count == 0 or bounding_box_min.x >
+ // bounding_box_max.x (empty cell)
+ bool convex_hull_valid;
+ bool bounding_box_valid;
+
+ void clear() {
+ convex_hull.clear();
+ convex_hull_valid = false;
+ bounding_box_valid = false;
+ }
+};
+
+struct Cell {
+ // NULL-terminated string with cell name. The GDSII specification allows
+ // only ASCII-encoded strings. The OASIS specification restricts the
+ // allowed characters to the range 0x21–0x7E (the space character, 0x20, is
+ // forbidden). Gdstk does NOT enforce either rule.
+ //
+ // Cells in a library are identified by their name, so names must be
+ // unique. This rule is not enforced in Gdstk, but it is assumed to be
+ // followed (various hash maps are built based on cell names).
+ char* name;
+
+ // Elements should be added to (or removed from) the cell using these arrays
+ Array<Polygon*> polygon_array;
+ Array<Reference*> reference_array;
+ Array<FlexPath*> flexpath_array;
+ Array<RobustPath*> robustpath_array;
+ Array<Label*> label_array;
+
+ Property* properties;
+
+ // Used by the python interface to store the associated PyObject* (if any).
+ // No functions in gdstk namespace should touch this value!
+ void* owner;
+
+ void init(const char* name_) { name = copy_string(name_, NULL); }
+
+ void print(bool all) const;
+
+ void clear();
+
+ void free_all() {
+ for (uint64_t j = 0; j < polygon_array.count; j++) {
+ polygon_array[j]->clear();
+ free_allocation(polygon_array[j]);
+ }
+ for (uint64_t j = 0; j < flexpath_array.count; j++) {
+ flexpath_array[j]->clear();
+ free_allocation(flexpath_array[j]);
+ }
+ for (uint64_t j = 0; j < robustpath_array.count; j++) {
+ robustpath_array[j]->clear();
+ free_allocation(robustpath_array[j]);
+ }
+ for (uint64_t j = 0; j < reference_array.count; j++) {
+ reference_array[j]->clear();
+ free_allocation(reference_array[j]);
+ }
+ for (uint64_t j = 0; j < label_array.count; j++) {
+ label_array[j]->clear();
+ free_allocation(label_array[j]);
+ }
+ clear();
+ }
+
+ // Bounding box corners are returned in min and max. For an empty cell,
+ // return min.x > max.x. Internally, this function simply calls the
+ // caching version with an empty cache.
+ void bounding_box(Vec2& min, Vec2& max) const;
+ // Caching version of the bounding box calculation. The returned
+ // GeometryInfo is guaranteed to have the information about this cell
+ // instance (bounding_box_valid == true).
+ GeometryInfo bounding_box(Map<GeometryInfo>& cache) const;
+
+ // The convex hull of the cell is appended to result (it doesn't need to be
+ // empty). Internally, this function simply calls the caching version with
+ // an empty cache.
+ void convex_hull(Array<Vec2>& result) const;
+ // Caching version of the convex hull calculation.
+ GeometryInfo convex_hull(Map<GeometryInfo>& cache) const;
+
+ // This cell instance must be zeroed before copy_from. If a new_name is
+ // NULL, use the same name as the source cell. If deep_copy == true, new
+ // elements (polygons, paths, references, and labels) are allocated and
+ // copied from the source cell. Otherwise, the same pointers are used.
+ void copy_from(const Cell& cell, const char* new_name, bool deep_copy);
+
+ // Append a (newly allocated) copy of all the polygons in the cell to
+ // result. If paths are included, their polygonal representation is
+ // calculated and also appended. Polygons from references are included up
+ // to depth levels, i.e., if depth == 0, no polygons from references are
+ // included, depth == 1 includes polygons from referenced cells (with their
+ // transformation properly applied), but not from references thereof, and
+ // so on. Depth < 0, removes the limit in the recursion depth. If filter
+ // is true, only polygons with the indicated tag are appended.
+ void get_polygons(bool apply_repetitions, bool include_paths, int64_t depth, bool filter,
+ Tag tag, Array<Polygon*>& result) const;
+
+ // Similar to get_polygons, but for paths and labels.
+ void get_flexpaths(bool apply_repetitions, int64_t depth, bool filter, Tag tag,
+ Array<FlexPath*>& result) const;
+ void get_robustpaths(bool apply_repetitions, int64_t depth, bool filter, Tag tag,
+ Array<RobustPath*>& result) const;
+ void get_labels(bool apply_repetitions, int64_t depth, bool filter, Tag tag,
+ Array<Label*>& result) const;
+
+ // Insert all dependencies in result. Dependencies are cells that appear
+ // in this cell's references. If recursive, include the whole dependency
+ // tree (dependencies of dependencies).
+ void get_dependencies(bool recursive, Map<Cell*>& result) const;
+ void get_raw_dependencies(bool recursive, Map<RawCell*>& result) const;
+
+ // Append all tags found in polygons and paths/labels to result.
+ // References are not included in the result.
+ void get_shape_tags(Set<Tag>& result) const;
+ void get_label_tags(Set<Tag>& result) const;
+
+ // Transform a cell hierarchy into a flat cell, with no dependencies, by
+ // inserting the elements from this cell's references directly into the
+ // cell (with the corresponding transformations). Removed references are
+ // appended to removed_references.
+ void flatten(bool apply_repetitions, Array<Reference*>& removed_references);
+
+ // Change the tags of all elements in this cell. Map keys are the current
+ // tags and map values are the desired new tags. Elements in references
+ // are not remapped (use get_dependencies to loop over and remap them).
+ void remap_tags(const TagMap& map);
+
+ // These functions output the cell and its contents in the GDSII and SVG
+ // formats. They are not supposed to be called by the user. Use
+ // Library.write_gds and Cell.write_svg instead.
+ ErrorCode to_gds(FILE* out, double scaling, uint64_t max_points, double precision,
+ const tm* timestamp) const;
+ ErrorCode to_svg(FILE* out, double scaling, uint32_t precision, const char* attributes,
+ PolygonComparisonFunction comp) const;
+
+ // Output this cell to filename in SVG format. The geometry is drawn in
+ // the default units (px), but can be scaled freely. Argument precision
+ // defines the maximum desired precision for floating point representation
+ // in the SVG file. Arguments shape_style and label_style, if not NULL,
+ // can de used to customize the SVG style of elements by tag. If
+ // background is not NULL, it should be a valid SVG color for the image
+ // background. Argument pad defines the margin (in px) added around the
+ // cell bounding box, unless pad_as_percentage == true, in which case it is
+ // interpreted as a percentage of the largest bounding box dimension.
+ // Argument comp in to_svg can be used to sort the polygons in the SVG
+ // output, which affects their draw order.
+ ErrorCode write_svg(const char* filename, double scaling, uint32_t precision,
+ StyleMap* shape_style, StyleMap* label_style, const char* background,
+ double pad, bool pad_as_percentage, PolygonComparisonFunction comp) const;
+};
+
enum struct Operation { Or, And, Xor, Not };
+enum struct OffsetJoin { Miter, Bevel, Round };
+
+// The following operations are executed in an integer grid of vertices, so the
+// geometry should be scaled by a large enough factor to garante a minimal
+// precision level. However, if the scaling factor is too large, it may cause
+// overflow of coordinates. Resulting polygons are appended to result.
+
+// Boolean (clipping) operations on polygons
+ErrorCode boolean(const Array<Polygon*>& polys1, const Array<Polygon*>& polys2, Operation operation,
+ double scaling, Array<Polygon*>& result);
+
+inline ErrorCode boolean(const Polygon& poly1, const Array<Polygon*>& polys2, Operation operation,
+ double scaling, Array<Polygon*>& result) {
+ const Polygon* p1 = &poly1;
+ const Array<Polygon*> polys1 = {1, 1, (Polygon**)&p1};
+ return boolean(polys1, polys2, operation, scaling, result);
+}
+
+inline ErrorCode boolean(const Array<Polygon*>& polys1, const Polygon& poly2, Operation operation,
+ double scaling, Array<Polygon*>& result) {
+ const Polygon* p2 = &poly2;
+ const Array<Polygon*> polys2 = {1, 1, (Polygon**)&p2};
+ return boolean(polys1, polys2, operation, scaling, result);
+}
+
+inline ErrorCode boolean(const Polygon& poly1, const Polygon& poly2, Operation operation,
+ double scaling, Array<Polygon*>& result) {
+ const Polygon* p1 = &poly1;
+ const Polygon* p2 = &poly2;
+ const Array<Polygon*> polys1 = {1, 1, (Polygon**)&p1};
+ const Array<Polygon*> polys2 = {1, 1, (Polygon**)&p2};
+ return boolean(polys1, polys2, operation, scaling, result);
+}
+
+// Shorthand for joining a set of polygons
+inline ErrorCode merge(const Array<Polygon*>& polygons, double scaling, Array<Polygon*>& result) {
+ const Array<Polygon*> empty = {};
+ return boolean(polygons, empty, Operation::Or, scaling, result);
+}
+
+// Dilates or erodes polygons according to distance (negative distance results
+// in erosion). The effects of internal polygon edges (in polygons with holes,
+// for example) can be suppressed by setting use_union to true. Resulting
+// polygons are appended to result.
+ErrorCode offset(const Array<Polygon*>& polys, double distance, OffsetJoin join, double tolerance,
+ double scaling, bool use_union, Array<Polygon*>& result);
+
+inline ErrorCode offset(const Polygon& poly, double distance, OffsetJoin join, double tolerance,
+ double scaling, bool use_union, Array<Polygon*>& result) {
+ const Polygon* p = &poly;
+ const Array<Polygon*> polys = {1, 1, (Polygon**)&p};
+ return offset(polys, distance, join, tolerance, scaling, use_union, result);
+}
+
+// Slice the given polygon along the coordinates in positions. Positions must be sorted. Cuts are
+// vertical (horizontal) when x_axis is set to true (false). Argument result must be an array with
+// length at least positions.count + 1. The resulting slices are appended to the arrays in their
+// respective bins.
+ErrorCode slice(const Polygon& polygon, const Array<double>& positions, bool x_axis, double scaling,
+ Array<Polygon*>* result);
+
struct CurveInstruction {
+ union {
+ char command;
+ double number;
+ };
+};
+
+// Curves can used to build complex shapes by concatenating straight or curved
+// sections. Once complete, their point_array can be used to create a polygon.
+// They are also used to build the FlexPath spine.
+struct Curve {
+ // Before appending any section to a curve, it must contain at least 1
+ // point in its point_array
+ Array<Vec2> point_array;
+
+ // Tolerance for approximating curved sections with straight lines
+ double tolerance;
+
+ // Used internally. Keep record of the last Bézier control point, which is
+ // used for smooth continuations.
+ Vec2 last_ctrl;
+
+ // Used by the python interface to store the associated PyObject* (if any).
+ // No functions in gdstk namespace should touch this value!
+ void* owner;
+
+ void print(bool all) const;
+
+ // Point array must be in a valid state before initialization
+ void init(const Vec2 initial_position, double tolerance_) {
+ point_array.append(initial_position);
+ tolerance = tolerance_;
+ }
+
+ // This curve instance must be zeroed before copy_from
+ void copy_from(const Curve& curve) {
+ point_array.copy_from(curve.point_array);
+ tolerance = curve.tolerance;
+ last_ctrl = curve.last_ctrl;
+ }
+
+ void clear() { point_array.clear(); }
+
+ void append(const Vec2 v) { point_array.append(v); }
+
+ void append_unsafe(const Vec2 v) { point_array.append_unsafe(v); }
+
+ void remove(uint64_t index) { point_array.remove(index); }
+
+ void ensure_slots(uint64_t free_slots) { point_array.ensure_slots(free_slots); }
+
+ // A curve is considered closed if the distance between the first and last
+ // points is less then of equal to tolerance
+ bool closed() const {
+ if (point_array.count < 2) return false;
+ const Vec2 v = point_array[0] - point_array[point_array.count - 1];
+ return v.length_sq() <= tolerance * tolerance;
+ }
+
+ // In the following functions, the flag relative indicates whether the
+ // points are given relative to the last point in the curve. If multiple
+ // coordinates are used, all coordinates are relative to the same reference
+ // (last curve point at the time of execution)
+ void horizontal(double coord_x, bool relative);
+ void horizontal(const Array<double> coord_x, bool relative);
+ void vertical(double coord_y, bool relative);
+ void vertical(const Array<double> coord_y, bool relative);
+ void segment(Vec2 end_point, bool relative);
+ void segment(const Array<Vec2> points, bool relative);
+
+ // Every 3 points define a cubic Bézier section with 2 control points
+ // followed by the section end point (starting point being the current last
+ // point of the curve)
+ void cubic(const Array<Vec2> points, bool relative);
+
+ // Every 2 points define a cubic Bézier section with 1 control point
+ // followed by the section end point (starting point being the current last
+ // point of the curve and the first control calculated from the previous
+ // curve section for continuity)
+ void cubic_smooth(const Array<Vec2> points, bool relative);
+
+ // Every 2 points define a quadratic Bézier section with 1 control point
+ // followed by the section end point (starting point being the current last
+ // point of the curve)
+ void quadratic(const Array<Vec2> points, bool relative);
+
+ // Quadratic Bézier section with control point calculated from the previous
+ // curve section for continuity
+ void quadratic_smooth(Vec2 end_point, bool relative);
+
+ // Every point is used as the end point of a quadratic Bézier section (the
+ // control point is calculated from the previous curve section for
+ // continuity)
+ void quadratic_smooth(const Array<Vec2> points, bool relative);
+
+ // Single Bézier section defined by any number of control points
+ void bezier(const Array<Vec2> points, bool relative);
+
+ // Create a smooth interpolation curve through points using cubic Bézier
+ // sections. The angle at any point i can be constrained to the value
+ // angles[i] if angle_constraints[i] is true. The tension array controls
+ // the input and output tensions of the curve at each point (the input at
+ // the first point and output at the last point are only meaningful for
+ // closed curves). Because the first point of the interpolation is the
+ // last point in the curve, the lengths of the arrays angles,
+ // angle_constraints, and tension must be points.count + 1. No argument
+ // can be NULL.
+ void interpolation(const Array<Vec2> points, double* angles, bool* angle_constraints,
+ Vec2* tension, double initial_curl, double final_curl, bool cycle,
+ bool relative);
+
+ // Add an elliptical arc to the curve. Argument rotation is used to rotate
+ // the axes of the ellipse.
+ void arc(double radius_x, double radius_y, double initial_angle, double final_angle,
+ double rotation);
+
+ // Add a parametric curve section to the curve. If relative is true,
+ // curve_function(0, data) should be (0, 0) for the curve to be continuous.
+ void parametric(ParametricVec2 curve_function, void* data, bool relative);
+
+ // Short-hand function for appending several sections at once. Array items
+ // must be formed by a series of instruction characters followed by the
+ // correct number of arguments for that instruction. Instruction
+ // characters and arguments are:
+ // - Line segment: 'L', x, y
+ // - Horizontal segment: 'H', x
+ // - Vertical segment: 'V', y
+ // - Cubic Bézier: 'C', x0, y0, x1, y1, x2, y2
+ // - Smooth cubic Bézier: 'S', x0, y0, x1, y1
+ // - Quadratic Bézier: 'Q', x0, y0, x1, y1
+ // - Smooth quadratic Bézier: 'T', x, y
+ // - Elliptical arc: 'E', rad0, rad1, angle0, angle1, rotation
+ // - Circular arc: 'A', radius, angle0, angle1
+ // - Circular turn: 'a', radius, angle
+ // Coordinates in instructions L, H, V, C, S, Q and T are absolute. Lower
+ // case versions of those instructions can be used for relative coordinates
+ // (in this case, each section will be relative to the previous). Return
+ // the number of items processed. If n < count, item n could not be
+ // parsed.
+ uint64_t commands(const CurveInstruction* items, uint64_t count);
+
+ // Add a circular arc to the curve ensuring continuity. Positive
+ // (negative) angles create counter-clockwise (clockwise) turns.
+ void turn(double radius, double angle) {
+ const Vec2 direction = point_array[point_array.count - 1] - last_ctrl;
+ double initial_angle = direction.angle() + (angle < 0 ? 0.5 * M_PI : -0.5 * M_PI);
+ arc(radius, radius, initial_angle, initial_angle + angle, 0);
+ }
+
+ private:
+ void append_cubic(const Vec2 p0, const Vec2 p1, const Vec2 p2, const Vec2 p3);
+ void append_quad(const Vec2 p0, const Vec2 p1, const Vec2 p2);
+ void append_bezier(const Array<Vec2> ctrl);
+};
+
// A flexpath holds a spine and any number of elements. The spine dictates the
+// general shape of the path, but it is a simple curve, it doesn't have
+// information about width. The elements are the concrete paths that are
+// created based on the spine, a width and, optionally, an offset from the
+// spine. Both width and offset can change along the spine.
+
+struct FlexPathElement {
+ Tag tag;
+
+ // Array of widths and offsets for this path element. The array count must
+ // match the spine count. Each Vec2 v holds the width of the path divided
+ // by 2 in v.e[0] and the path offset in v.e[1] for the respective point
+ // along the spine.
+ Array<Vec2> half_width_and_offset;
+
+ JoinType join_type;
+ JoinFunction join_function;
+ void* join_function_data; // User data passed directly to join_function
+
+ EndType end_type;
+ Vec2 end_extensions;
+ EndFunction end_function;
+ void* end_function_data; // User data passed directly to end_function
+
+ BendType bend_type;
+ double bend_radius;
+ BendFunction bend_function;
+ void* bend_function_data; // User data passed directly to bend_function
+};
+
+struct FlexPath {
+ Curve spine;
+ FlexPathElement* elements; // Array with count num_elements
+ uint64_t num_elements;
+
+ // If simple_path is true, all elements will be treated as if they have
+ // constant widths (using the first elements in their respective
+ // half_width_and_offset arrays) and saved as paths, not polygonal
+ // boundaries.
+ bool simple_path;
+
+ // Flag indicating whether the width of the path elements should be scaled
+ // when scaling the path (manually or through references).
+ bool scale_width;
+
+ Repetition repetition;
+ Property* properties;
+
+ RaithData raith_data;
+
+ // Used by the python interface to store the associated PyObject* (if any).
+ // No functions in gdstk namespace should touch this value!
+ void* owner;
+
+ // These are initialization routines to facilitate the creation of new
+ // flexpaths. In versions with argument num_elements_, the elements array
+ // will be dynamically allocated (and num_elements properly set).
+ // Otherwise, num_elements and elements are expected to be already
+ // allocated and set. Arguments width, offset and tag can be single values
+ // (which are applied to all elements) or arrays with count num_elements,
+ // one value for each path element. Argument separation is the desired
+ // distance between adjacent elements.
+ void init(const Vec2 initial_position, double width, double offset, double tolerance, Tag tag);
+ void init(const Vec2 initial_position, const double* width, const double* offset,
+ double tolerance, const Tag* tag);
+ void init(const Vec2 initial_position, uint64_t num_elements_, double width, double separation,
+ double tolerance, Tag tag);
+ void init(const Vec2 initial_position, uint64_t num_elements_, const double* width,
+ const double* offset, double tolerance, const Tag* tag);
+
+ void print(bool all) const;
+
+ void clear();
+
+ // This path instance must be zeroed before copy_from
+ void copy_from(const FlexPath& path);
+
+ void translate(const Vec2 v);
+ void scale(double scale, const Vec2 center);
+ void mirror(const Vec2 p0, const Vec2 p1);
+ void rotate(double angle, const Vec2 center);
+
+ // Transformations are applied in the order of arguments, starting with
+ // magnification and translating by origin at the end. This is equivalent
+ // to the transformation defined by a Reference with the same arguments.
+ void transform(double magnification, bool x_reflection, double rotation, const Vec2 origin);
+
+ // Append the copies of this path defined by its repetition to result.
+ void apply_repetition(Array<FlexPath*>& result);
+
+ // These functions are equivalent to those for curves (curve.h), with the
+ // addition of width and offset, which can be NULL (no width or offset
+ // changes) or arrays with count num_elements.
+ void horizontal(double coord_x, const double* width, const double* offset, bool relative);
+ void horizontal(const Array<double> coord_x, const double* width, const double* offset,
+ bool relative);
+ void vertical(double coord_y, const double* width, const double* offset, bool relative);
+ void vertical(const Array<double> coord_y, const double* width, const double* offset,
+ bool relative);
+ void segment(Vec2 end_point, const double* width, const double* offset, bool relative);
+ void segment(const Array<Vec2> point_array, const double* width, const double* offset,
+ bool relative);
+ void cubic(const Array<Vec2> point_array, const double* width, const double* offset,
+ bool relative);
+ void cubic_smooth(const Array<Vec2> point_array, const double* width, const double* offset,
+ bool relative);
+ void quadratic(const Array<Vec2> point_array, const double* width, const double* offset,
+ bool relative);
+ void quadratic_smooth(Vec2 end_point, const double* width, const double* offset, bool relative);
+ void quadratic_smooth(const Array<Vec2> point_array, const double* width, const double* offset,
+ bool relative);
+ void bezier(const Array<Vec2> point_array, const double* width, const double* offset,
+ bool relative);
+ void interpolation(const Array<Vec2> point_array, double* angles, bool* angle_constraints,
+ Vec2* tension, double initial_curl, double final_curl, bool cycle,
+ const double* width, const double* offset, bool relative);
+ void arc(double radius_x, double radius_y, double initial_angle, double final_angle,
+ double rotation, const double* width, const double* offset);
+ void turn(double radius, double angle, const double* width, const double* offset);
+ void parametric(ParametricVec2 curve_function, void* data, const double* width,
+ const double* offset, bool relative);
+ uint64_t commands(const CurveInstruction* items, uint64_t count);
+
+ // Append the polygonal representation of this path to result. If filter
+ // is true, only elements with the indicated tag are processed.
+ // Overlapping points are removed from the path before any processing is
+ // executed.
+ ErrorCode to_polygons(bool filter, Tag tag, Array<Polygon*>& result);
+
+ // Calculate the center of an element of this path and append the resulting
+ // curve to result.
+ ErrorCode element_center(const FlexPathElement* el, Array<Vec2>& result);
+
+ // These functions output the polygon in the GDSII, OASIS and SVG formats.
+ // They are not supposed to be called by the user. Because fracturing
+ // occurs at cell_to_gds, the polygons must be checked there and, if
+ // needed, fractured. Therefore, to_gds should be used only when
+ // simple_path == true to produce true GDSII path elements. The same is
+ // valid for to_oas, even though no fracturing ever occurs for OASIS files.
+ ErrorCode to_gds(FILE* out, double scaling);
+ ErrorCode to_oas(OasisStream& out, OasisState& state);
+ ErrorCode to_svg(FILE* out, double scaling, uint32_t precision);
+
+ private:
+ void remove_overlapping_points();
+ void fill_offsets_and_widths(const double* width, const double* offset);
+};
+
// Public Domain version of the unscii-16 font from http://pelulamu.net/unscii/
+// codepoints 0x21 thru 0x7F
+
+// This header contains the data for the default font in Gdstk, used in the
+// text function. Given a desired code point CP within the range of defined
+// code points, we use it to index into arrays _num_polys and _first_poly:
+//
+// _num_poly[CP - FIRST_CODEPOINT] = p_count is the number of polygons in the
+// requested glyph.
+//
+// _first_poly[CP - FIRST_CODEPOINT] = p_idx is the index into _num_coords and
+// _first_coord.
+//
+// _num_coords[p_idx] = c_count is the number of coordinates in polygon p_idx
+// (counting x and y separately, so it is always an even number). The polygon
+// p_idx can be drawn using c_count coordinates from _first_coord:
+//
+// (_first_coord[p_idx], _first_coord[p_idx + 1]),
+// …,
+// (_first_coord[p_idx + c_count - 2], _first_coord[p_idx + c_count - 1])
+//
+// To draw the remaining polygons in the glyph, keep incrementing p_idx to draw
+// all p_count polygons.
+
+// Font metrics
+#define FIRST_CODEPOINT 0x21
+#define HORIZONTAL_STEP 9
+#define HORIZONTAL_TAB (4 * HORIZONTAL_STEP)
+#define HORIZONTAL_LINESKIP 20
+#define VERTICAL_STEP (2 * HORIZONTAL_STEP)
+#define VERTICAL_TAB (4 * VERTICAL_STEP)
+#define VERTICAL_LINESKIP 16
+
+const Vec2 _all_coords[] = {
+ 5, 7, 5, 15, 3, 15, 3, 7, 5, 3, 5, 5, 3, 5, 3, 3, 3, 12, 3, 15, 1, 15, 1, 12, 7, 12,
+ 7, 15, 5, 15, 5, 12, 3, 3, 3, 6, 4, 6, 4, 3, 6, 3, 6, 6, 7, 6, 7, 7, 6, 7, 6, 10,
+ 7, 10, 7, 11, 6, 11, 6, 14, 4, 14, 4, 11, 3, 11, 3, 14, 1, 14, 1, 11, 0, 11, 0, 10, 1, 10,
+ 1, 7, 3, 7, 3, 10, 4, 10, 4, 7, 3, 7, 1, 7, 0, 7, 0, 6, 1, 6, 1, 3, 2, 4, 3, 4,
+ 3, 2, 5, 2, 5, 4, 6, 4, 6, 5, 7, 5, 7, 7, 6, 7, 6, 8, 5, 8, 5, 9, 4, 9, 4, 10,
+ 3, 10, 3, 12, 5, 12, 5, 11, 7, 11, 7, 12, 6, 12, 6, 13, 5, 13, 5, 15, 3, 15, 3, 13, 2, 13,
+ 2, 12, 1, 12, 1, 10, 2, 10, 2, 9, 3, 9, 3, 8, 4, 8, 4, 7, 5, 7, 5, 5, 3, 5, 3, 6,
+ 1, 6, 1, 5, 2, 5, 4, 12, 4, 10, 3, 10, 3, 8, 2, 8, 2, 6, 1, 6, 1, 4, 0, 4, 0, 2,
+ 2, 2, 2, 4, 3, 4, 3, 6, 4, 6, 4, 8, 5, 8, 5, 10, 6, 10, 6, 12, 7, 12, 7, 14, 5, 14,
+ 5, 12, 2, 10, 2, 13, 0, 13, 0, 10, 7, 3, 7, 6, 5, 6, 5, 3, 1, 9, 1, 8, 0, 8, 0, 4,
+ 1, 4, 1, 3, 4, 3, 4, 4, 2, 4, 2, 8, 3, 8, 3, 7, 4, 7, 4, 4, 5, 4, 5, 3, 7, 3,
+ 7, 4, 6, 4, 6, 7, 7, 7, 7, 9, 6, 9, 6, 8, 5, 8, 5, 9, 4, 9, 4, 10, 5, 10, 5, 11,
+ 6, 11, 6, 13, 5, 13, 5, 14, 2, 14, 2, 13, 1, 13, 1, 11, 3, 11, 3, 13, 4, 13, 4, 11, 3, 11,
+ 1, 11, 2, 11, 2, 9, 2, 12, 2, 11, 4, 11, 4, 12, 5, 12, 5, 15, 3, 15, 3, 12, 3, 12, 2, 12,
+ 2, 5, 3, 5, 3, 3, 4, 3, 4, 2, 6, 2, 6, 3, 5, 3, 5, 5, 4, 5, 4, 12, 5, 12, 5, 14,
+ 6, 14, 6, 15, 4, 15, 4, 14, 3, 14, 3, 12, 4, 12, 4, 5, 3, 5, 3, 3, 2, 3, 2, 2, 4, 2,
+ 4, 3, 5, 3, 5, 5, 6, 5, 6, 12, 5, 12, 5, 14, 4, 14, 4, 15, 2, 15, 2, 14, 3, 14, 2, 10,
+ 2, 9, 0, 9, 0, 8, 2, 8, 2, 7, 1, 7, 1, 5, 3, 5, 3, 7, 5, 7, 5, 5, 7, 5, 7, 7,
+ 6, 7, 6, 8, 8, 8, 8, 9, 6, 9, 6, 10, 7, 10, 7, 12, 5, 12, 5, 10, 3, 10, 3, 12, 1, 12,
+ 1, 10, 1, 9, 1, 8, 3, 8, 3, 5, 5, 5, 5, 8, 7, 8, 7, 9, 5, 9, 5, 12, 3, 12, 3, 9,
+ 3, 3, 2, 3, 2, 2, 1, 2, 1, 1, 3, 1, 3, 2, 4, 2, 4, 3, 5, 3, 5, 6, 2, 6, 2, 5,
+ 3, 5, 7, 9, 1, 9, 1, 8, 7, 8, 5, 3, 5, 6, 3, 6, 3, 3, 5, 14, 5, 12, 4, 12, 4, 10,
+ 3, 10, 3, 8, 2, 8, 2, 6, 1, 6, 1, 4, 0, 4, 0, 2, 2, 2, 2, 4, 3, 4, 3, 6, 4, 6,
+ 4, 8, 5, 8, 5, 10, 6, 10, 6, 12, 7, 12, 7, 14, 8, 14, 8, 16, 6, 16, 6, 14, 6, 5, 7, 5,
+ 7, 12, 6, 12, 6, 13, 5, 13, 5, 14, 2, 14, 2, 13, 1, 13, 1, 12, 0, 12, 0, 5, 2, 5, 2, 7,
+ 3, 7, 3, 8, 2, 8, 2, 12, 3, 12, 3, 13, 4, 13, 4, 12, 5, 12, 5, 10, 4, 10, 4, 9, 5, 9,
+ 5, 5, 4, 5, 4, 4, 3, 4, 3, 5, 2, 5, 0, 5, 1, 5, 1, 4, 2, 4, 2, 3, 5, 3, 5, 4,
+ 6, 4, 4, 9, 3, 9, 3, 8, 4, 8, 2, 12, 1, 12, 1, 11, 3, 11, 3, 4, 1, 4, 1, 3, 7, 3,
+ 7, 4, 5, 4, 5, 14, 3, 14, 3, 13, 2, 13, 1, 11, 3, 11, 3, 13, 5, 13, 5, 9, 4, 9, 4, 8,
+ 3, 8, 3, 7, 2, 7, 2, 6, 1, 6, 1, 3, 7, 3, 7, 4, 3, 4, 3, 6, 4, 6, 4, 7, 5, 7,
+ 5, 8, 6, 8, 6, 9, 7, 9, 7, 13, 6, 13, 6, 14, 2, 14, 2, 13, 1, 13, 2, 4, 2, 3, 6, 3,
+ 6, 4, 7, 4, 7, 8, 6, 8, 6, 9, 7, 9, 7, 13, 6, 13, 6, 14, 2, 14, 2, 13, 1, 13, 1, 11,
+ 3, 11, 3, 13, 5, 13, 5, 9, 3, 9, 3, 8, 5, 8, 5, 4, 3, 4, 3, 6, 1, 6, 1, 4, 6, 3,
+ 6, 7, 7, 7, 7, 8, 6, 8, 6, 14, 4, 14, 4, 13, 3, 13, 3, 12, 2, 12, 2, 11, 1, 11, 1, 10,
+ 0, 10, 0, 8, 2, 8, 2, 10, 3, 10, 3, 11, 4, 11, 4, 8, 2, 8, 0, 8, 0, 7, 4, 7, 4, 3,
+ 2, 4, 2, 3, 6, 3, 6, 4, 7, 4, 7, 9, 6, 9, 6, 10, 3, 10, 3, 13, 7, 13, 7, 14, 1, 14,
+ 1, 9, 5, 9, 5, 4, 3, 4, 3, 6, 1, 6, 1, 4, 7, 9, 6, 9, 6, 10, 3, 10, 3, 12, 4, 12,
+ 4, 13, 6, 13, 6, 14, 3, 14, 3, 13, 2, 13, 2, 12, 1, 12, 1, 4, 3, 4, 3, 9, 5, 9, 5, 4,
+ 3, 4, 1, 4, 2, 4, 2, 3, 6, 3, 6, 4, 7, 4, 5, 10, 4, 10, 4, 8, 3, 8, 3, 3, 5, 3,
+ 5, 8, 6, 8, 6, 10, 7, 10, 7, 14, 1, 14, 1, 13, 5, 13, 7, 8, 6, 8, 6, 9, 7, 9, 7, 13,
+ 6, 13, 6, 14, 2, 14, 2, 13, 1, 13, 1, 10, 3, 10, 3, 13, 5, 13, 5, 9, 4, 9, 4, 10, 3, 10,
+ 1, 10, 1, 9, 2, 9, 2, 8, 1, 8, 1, 4, 3, 4, 3, 8, 4, 8, 4, 7, 5, 7, 5, 4, 3, 4,
+ 1, 4, 2, 4, 2, 3, 6, 3, 6, 4, 7, 4, 5, 5, 4, 5, 4, 4, 2, 4, 2, 3, 5, 3, 5, 4,
+ 6, 4, 6, 5, 7, 5, 7, 13, 6, 13, 6, 14, 2, 14, 2, 13, 1, 13, 1, 9, 3, 9, 3, 13, 5, 13,
+ 5, 9, 3, 9, 1, 9, 2, 9, 2, 8, 5, 8, 5, 10, 5, 13, 3, 13, 3, 10, 5, 3, 5, 6, 3, 6,
+ 3, 3, 5, 10, 5, 13, 3, 13, 3, 10, 3, 3, 2, 3, 2, 2, 1, 2, 1, 1, 3, 1, 3, 2, 4, 2,
+ 4, 3, 5, 3, 5, 6, 2, 6, 2, 5, 3, 5, 4, 11, 3, 11, 3, 10, 2, 10, 2, 9, 1, 9, 1, 8,
+ 2, 8, 2, 7, 3, 7, 3, 6, 4, 6, 4, 5, 5, 5, 5, 4, 7, 4, 7, 5, 6, 5, 6, 6, 5, 6,
+ 5, 7, 4, 7, 4, 8, 3, 8, 3, 9, 4, 9, 4, 10, 5, 10, 5, 11, 6, 11, 6, 12, 7, 12, 7, 13,
+ 5, 13, 5, 12, 4, 12, 7, 11, 1, 11, 1, 10, 7, 10, 7, 7, 1, 7, 1, 6, 7, 6, 2, 11, 3, 11,
+ 3, 10, 4, 10, 4, 9, 5, 9, 5, 8, 4, 8, 4, 7, 3, 7, 3, 6, 2, 6, 2, 5, 1, 5, 1, 4,
+ 3, 4, 3, 5, 4, 5, 4, 6, 5, 6, 5, 7, 6, 7, 6, 8, 7, 8, 7, 9, 6, 9, 6, 10, 5, 10,
+ 5, 11, 4, 11, 4, 12, 3, 12, 3, 13, 1, 13, 1, 12, 2, 12, 1, 12, 3, 12, 3, 14, 5, 14, 5, 11,
+ 4, 11, 4, 10, 3, 10, 3, 7, 5, 7, 5, 10, 6, 10, 6, 11, 7, 11, 7, 14, 6, 14, 6, 15, 2, 15,
+ 2, 14, 1, 14, 5, 3, 5, 5, 3, 5, 3, 3, 0, 4, 1, 4, 1, 3, 6, 3, 6, 4, 2, 4, 2, 13,
+ 5, 13, 5, 10, 3, 10, 3, 6, 6, 6, 6, 7, 7, 7, 7, 13, 6, 13, 6, 14, 1, 14, 1, 13, 0, 13,
+ 3, 3, 3, 8, 5, 8, 5, 3, 7, 3, 7, 12, 6, 12, 6, 13, 5, 13, 5, 14, 3, 14, 3, 13, 2, 13,
+ 2, 12, 1, 12, 1, 9, 3, 9, 3, 12, 5, 12, 5, 9, 3, 9, 1, 9, 1, 3, 7, 7, 6, 7, 6, 8,
+ 5, 8, 5, 9, 6, 9, 6, 10, 7, 10, 7, 13, 6, 13, 6, 14, 1, 14, 1, 9, 3, 9, 3, 13, 5, 13,
+ 5, 10, 4, 10, 4, 9, 3, 9, 1, 9, 1, 4, 3, 4, 3, 8, 4, 8, 4, 7, 5, 7, 5, 4, 3, 4,
+ 1, 4, 1, 3, 6, 3, 6, 4, 7, 4, 1, 4, 2, 4, 2, 3, 6, 3, 6, 4, 7, 4, 7, 6, 5, 6,
+ 5, 4, 3, 4, 3, 13, 5, 13, 5, 11, 7, 11, 7, 13, 6, 13, 6, 14, 2, 14, 2, 13, 1, 13, 6, 5,
+ 7, 5, 7, 12, 6, 12, 6, 13, 5, 13, 5, 14, 1, 14, 1, 4, 3, 4, 3, 13, 4, 13, 4, 12, 5, 12,
+ 5, 5, 4, 5, 4, 4, 3, 4, 1, 4, 1, 3, 5, 3, 5, 4, 6, 4, 7, 3, 7, 4, 3, 4, 3, 8,
+ 6, 8, 6, 9, 3, 9, 3, 13, 7, 13, 7, 14, 1, 14, 1, 3, 3, 3, 3, 9, 6, 9, 6, 10, 3, 10,
+ 3, 13, 7, 13, 7, 14, 1, 14, 1, 3, 1, 4, 2, 4, 2, 3, 7, 3, 7, 9, 4, 9, 4, 8, 5, 8,
+ 5, 4, 3, 4, 3, 13, 5, 13, 5, 11, 7, 11, 7, 13, 6, 13, 6, 14, 2, 14, 2, 13, 1, 13, 3, 3,
+ 3, 8, 5, 8, 5, 3, 7, 3, 7, 14, 5, 14, 5, 9, 3, 9, 3, 14, 1, 14, 1, 3, 1, 4, 1, 3,
+ 7, 3, 7, 4, 5, 4, 5, 13, 7, 13, 7, 14, 1, 14, 1, 13, 3, 13, 3, 4, 2, 4, 2, 3, 6, 3,
+ 6, 4, 7, 4, 7, 14, 5, 14, 5, 4, 3, 4, 3, 6, 1, 6, 1, 4, 2, 3, 2, 8, 3, 8, 3, 7,
+ 4, 7, 4, 5, 5, 5, 5, 3, 7, 3, 7, 5, 6, 5, 6, 7, 5, 7, 5, 8, 4, 8, 4, 9, 5, 9,
+ 5, 10, 6, 10, 6, 12, 7, 12, 7, 14, 5, 14, 5, 12, 4, 12, 4, 10, 3, 10, 3, 9, 2, 9, 2, 14,
+ 0, 14, 0, 3, 7, 3, 7, 4, 3, 4, 3, 14, 1, 14, 1, 3, 2, 3, 2, 10, 3, 10, 3, 8, 4, 8,
+ 4, 10, 5, 10, 5, 3, 7, 3, 7, 14, 5, 14, 5, 13, 4, 13, 4, 11, 3, 11, 3, 13, 2, 13, 2, 14,
+ 0, 14, 0, 3, 2, 3, 2, 8, 3, 8, 3, 7, 4, 7, 4, 5, 5, 5, 5, 3, 7, 3, 7, 14, 5, 14,
+ 5, 9, 4, 9, 4, 10, 3, 10, 3, 12, 2, 12, 2, 14, 0, 14, 0, 3, 7, 13, 6, 13, 6, 14, 2, 14,
+ 2, 13, 1, 13, 1, 4, 3, 4, 3, 13, 5, 13, 5, 4, 3, 4, 1, 4, 2, 4, 2, 3, 6, 3, 6, 4,
+ 7, 4, 3, 3, 3, 8, 6, 8, 6, 9, 7, 9, 7, 13, 6, 13, 6, 14, 1, 14, 1, 9, 3, 9, 3, 13,
+ 5, 13, 5, 9, 3, 9, 1, 9, 1, 3, 4, 2, 5, 2, 5, 1, 7, 1, 7, 2, 6, 2, 6, 4, 7, 4,
+ 7, 13, 6, 13, 6, 14, 2, 14, 2, 13, 1, 13, 1, 4, 3, 4, 3, 13, 5, 13, 5, 4, 3, 4, 1, 4,
+ 2, 4, 2, 3, 4, 3, 3, 3, 3, 8, 4, 8, 4, 7, 5, 7, 5, 3, 7, 3, 7, 7, 6, 7, 6, 9,
+ 7, 9, 7, 13, 6, 13, 6, 14, 1, 14, 1, 9, 3, 9, 3, 13, 5, 13, 5, 9, 3, 9, 1, 9, 1, 3,
+ 2, 4, 2, 3, 6, 3, 6, 4, 7, 4, 7, 7, 6, 7, 6, 8, 5, 8, 5, 9, 4, 9, 4, 10, 3, 10,
+ 3, 13, 5, 13, 5, 11, 7, 11, 7, 13, 6, 13, 6, 14, 2, 14, 2, 13, 1, 13, 1, 10, 2, 10, 2, 9,
+ 3, 9, 3, 8, 4, 8, 4, 7, 5, 7, 5, 4, 3, 4, 3, 6, 1, 6, 1, 4, 5, 3, 5, 13, 7, 13,
+ 7, 14, 1, 14, 1, 13, 3, 13, 3, 3, 2, 4, 2, 3, 6, 3, 6, 4, 7, 4, 7, 14, 5, 14, 5, 4,
+ 3, 4, 3, 14, 1, 14, 1, 4, 2, 7, 2, 5, 3, 5, 3, 3, 5, 3, 5, 5, 6, 5, 6, 7, 7, 7,
+ 7, 14, 5, 14, 5, 7, 3, 7, 3, 14, 1, 14, 1, 7, 2, 3, 2, 4, 3, 4, 3, 6, 4, 6, 4, 4,
+ 5, 4, 5, 3, 7, 3, 7, 14, 5, 14, 5, 7, 4, 7, 4, 9, 3, 9, 3, 7, 2, 7, 2, 14, 0, 14,
+ 0, 3, 1, 12, 1, 11, 2, 11, 2, 10, 3, 10, 3, 7, 2, 7, 2, 6, 1, 6, 1, 5, 0, 5, 0, 3,
+ 2, 3, 2, 5, 3, 5, 3, 6, 5, 6, 5, 5, 6, 5, 6, 3, 8, 3, 8, 5, 7, 5, 7, 6, 6, 6,
+ 6, 7, 5, 7, 5, 10, 6, 10, 6, 11, 7, 11, 7, 12, 8, 12, 8, 14, 6, 14, 6, 12, 5, 12, 5, 11,
+ 3, 11, 3, 12, 2, 12, 2, 14, 0, 14, 0, 12, 1, 12, 1, 10, 2, 10, 2, 9, 3, 9, 3, 3, 5, 3,
+ 5, 9, 6, 9, 6, 10, 7, 10, 7, 12, 8, 12, 8, 14, 6, 14, 6, 12, 5, 12, 5, 10, 3, 10, 3, 12,
+ 2, 12, 2, 14, 0, 14, 0, 12, 5, 11, 4, 11, 4, 9, 3, 9, 3, 8, 2, 8, 2, 6, 1, 6, 1, 3,
+ 7, 3, 7, 4, 3, 4, 3, 6, 4, 6, 4, 8, 5, 8, 5, 9, 6, 9, 6, 11, 7, 11, 7, 14, 1, 14,
+ 1, 13, 5, 13, 6, 2, 6, 3, 4, 3, 4, 14, 6, 14, 6, 15, 2, 15, 2, 2, 1, 14, 1, 12, 2, 12,
+ 2, 10, 3, 10, 3, 8, 4, 8, 4, 6, 5, 6, 5, 4, 6, 4, 6, 2, 8, 2, 8, 4, 7, 4, 7, 6,
+ 6, 6, 6, 8, 5, 8, 5, 10, 4, 10, 4, 12, 3, 12, 3, 14, 2, 14, 2, 16, 0, 16, 0, 14, 4, 3,
+ 2, 3, 2, 2, 6, 2, 6, 15, 2, 15, 2, 14, 4, 14, 2, 13, 1, 13, 1, 11, 0, 11, 0, 9, 2, 9,
+ 2, 11, 3, 11, 3, 13, 4, 13, 4, 11, 5, 11, 5, 9, 7, 9, 7, 11, 6, 11, 6, 13, 5, 13, 5, 14,
+ 4, 14, 4, 15, 3, 15, 3, 14, 2, 14, 8, 1, 0, 1, 0, 0, 8, 0, 4, 13, 4, 12, 5, 12, 5, 11,
+ 7, 11, 7, 12, 6, 12, 6, 13, 5, 13, 5, 15, 3, 15, 3, 13, 6, 9, 6, 10, 2, 10, 2, 9, 5, 9,
+ 5, 8, 2, 8, 2, 7, 1, 7, 1, 4, 3, 4, 3, 7, 5, 7, 5, 4, 3, 4, 1, 4, 2, 4, 2, 3,
+ 7, 3, 7, 9, 7, 9, 6, 9, 6, 10, 3, 10, 3, 14, 1, 14, 1, 4, 3, 4, 3, 9, 5, 9, 5, 4,
+ 3, 4, 1, 4, 1, 3, 6, 3, 6, 4, 7, 4, 1, 4, 2, 4, 2, 3, 6, 3, 6, 4, 7, 4, 7, 5,
+ 5, 5, 5, 4, 3, 4, 3, 9, 5, 9, 5, 8, 7, 8, 7, 9, 6, 9, 6, 10, 2, 10, 2, 9, 1, 9,
+ 5, 14, 5, 10, 2, 10, 2, 9, 1, 9, 1, 4, 3, 4, 3, 9, 5, 9, 5, 4, 3, 4, 1, 4, 2, 4,
+ 2, 3, 7, 3, 7, 14, 2, 4, 2, 3, 6, 3, 6, 4, 3, 4, 3, 6, 7, 6, 7, 9, 6, 9, 6, 10,
+ 2, 10, 2, 9, 1, 9, 1, 7, 3, 7, 3, 9, 5, 9, 5, 7, 3, 7, 1, 7, 1, 4, 2, 10, 1, 10,
+ 1, 9, 2, 9, 2, 3, 4, 3, 4, 9, 7, 9, 7, 10, 4, 10, 4, 13, 7, 13, 7, 14, 3, 14, 3, 13,
+ 2, 13, 5, 1, 1, 1, 1, 0, 6, 0, 6, 1, 7, 1, 7, 10, 2, 10, 2, 9, 1, 9, 1, 4, 3, 4,
+ 3, 9, 5, 9, 5, 4, 3, 4, 1, 4, 2, 4, 2, 3, 5, 3, 3, 3, 3, 9, 5, 9, 5, 3, 7, 3,
+ 7, 9, 6, 9, 6, 10, 3, 10, 3, 14, 1, 14, 1, 3, 5, 12, 5, 14, 3, 14, 3, 12, 3, 3, 7, 3,
+ 7, 4, 5, 4, 5, 10, 1, 10, 1, 9, 3, 9, 6, 12, 6, 14, 4, 14, 4, 12, 1, 1, 1, 0, 5, 0,
+ 5, 1, 6, 1, 6, 10, 4, 10, 4, 1, 3, 3, 3, 6, 4, 6, 4, 5, 5, 5, 5, 3, 7, 3, 7, 5,
+ 6, 5, 6, 6, 5, 6, 5, 7, 6, 7, 6, 8, 7, 8, 7, 10, 5, 10, 5, 8, 4, 8, 4, 7, 3, 7,
+ 3, 14, 1, 14, 1, 3, 3, 3, 7, 3, 7, 4, 5, 4, 5, 14, 1, 14, 1, 13, 3, 13, 2, 3, 2, 8,
+ 3, 8, 3, 4, 4, 4, 4, 8, 5, 8, 5, 3, 7, 3, 7, 9, 6, 9, 6, 10, 4, 10, 4, 9, 2, 9,
+ 2, 10, 0, 10, 0, 3, 3, 3, 3, 9, 5, 9, 5, 3, 7, 3, 7, 9, 6, 9, 6, 10, 1, 10, 1, 3,
+ 7, 9, 6, 9, 6, 10, 2, 10, 2, 9, 1, 9, 1, 4, 3, 4, 3, 9, 5, 9, 5, 4, 3, 4, 1, 4,
+ 2, 4, 2, 3, 6, 3, 6, 4, 7, 4, 3, 0, 3, 3, 6, 3, 6, 4, 7, 4, 7, 9, 6, 9, 6, 10,
+ 1, 10, 1, 4, 3, 4, 3, 9, 5, 9, 5, 4, 3, 4, 1, 4, 1, 0, 5, 0, 7, 0, 7, 10, 2, 10,
+ 2, 9, 1, 9, 1, 4, 3, 4, 3, 9, 5, 9, 5, 4, 3, 4, 1, 4, 2, 4, 2, 3, 5, 3, 3, 3,
+ 3, 9, 5, 9, 5, 7, 7, 7, 7, 9, 6, 9, 6, 10, 1, 10, 1, 3, 1, 7, 2, 7, 2, 6, 5, 6,
+ 5, 4, 1, 4, 1, 3, 6, 3, 6, 4, 7, 4, 7, 6, 6, 6, 6, 7, 3, 7, 3, 9, 7, 9, 7, 10,
+ 2, 10, 2, 9, 1, 9, 1, 10, 1, 9, 2, 9, 2, 4, 3, 4, 3, 3, 7, 3, 7, 4, 4, 4, 4, 9,
+ 7, 9, 7, 10, 4, 10, 4, 13, 2, 13, 2, 10, 2, 4, 2, 3, 7, 3, 7, 10, 5, 10, 5, 4, 3, 4,
+ 3, 10, 1, 10, 1, 4, 2, 5, 2, 4, 3, 4, 3, 3, 5, 3, 5, 4, 6, 4, 6, 5, 7, 5, 7, 10,
+ 5, 10, 5, 5, 3, 5, 3, 10, 1, 10, 1, 5, 1, 5, 1, 3, 3, 3, 3, 4, 4, 4, 4, 3, 6, 3,
+ 6, 5, 7, 5, 7, 10, 5, 10, 5, 5, 4, 5, 4, 8, 3, 8, 3, 5, 2, 5, 2, 10, 0, 10, 0, 5,
+ 1, 8, 1, 7, 2, 7, 2, 6, 1, 6, 1, 5, 0, 5, 0, 3, 2, 3, 2, 5, 3, 5, 3, 6, 4, 6,
+ 4, 5, 5, 5, 5, 3, 7, 3, 7, 5, 6, 5, 6, 6, 5, 6, 5, 7, 6, 7, 6, 8, 7, 8, 7, 10,
+ 5, 10, 5, 8, 4, 8, 4, 7, 3, 7, 3, 8, 2, 8, 2, 10, 0, 10, 0, 8, 2, 4, 2, 3, 5, 3,
+ 5, 1, 2, 1, 2, 0, 6, 0, 6, 1, 7, 1, 7, 10, 5, 10, 5, 4, 3, 4, 3, 10, 1, 10, 1, 4,
+ 5, 8, 4, 8, 4, 7, 3, 7, 3, 6, 2, 6, 2, 5, 1, 5, 1, 3, 7, 3, 7, 4, 3, 4, 3, 5,
+ 4, 5, 4, 6, 5, 6, 5, 7, 6, 7, 6, 8, 7, 8, 7, 10, 1, 10, 1, 9, 5, 9, 3, 9, 0, 9,
+ 0, 8, 3, 8, 3, 3, 4, 3, 4, 2, 7, 2, 7, 3, 5, 3, 5, 8, 4, 8, 4, 9, 5, 9, 5, 14,
+ 7, 14, 7, 15, 4, 15, 4, 14, 3, 14, 5, 2, 5, 16, 3, 16, 3, 2, 2, 9, 3, 9, 3, 8, 2, 8,
+ 2, 3, 0, 3, 0, 2, 3, 2, 3, 3, 4, 3, 4, 8, 7, 8, 7, 9, 4, 9, 4, 14, 3, 14, 3, 15,
+ 0, 15, 0, 14, 2, 14, 0, 12, 1, 12, 1, 13, 2, 13, 2, 14, 3, 14, 3, 12, 6, 12, 6, 13, 7, 13,
+ 7, 15, 6, 15, 6, 14, 5, 14, 5, 13, 4, 13, 4, 15, 1, 15, 1, 14, 0, 14, 2, 6, 2, 8, 1, 8,
+ 1, 14, 2, 14, 2, 16, 0, 16, 0, 6, 3, 8, 3, 14, 2, 14, 2, 8, 5, 10, 5, 2, 6, 2, 6, 10,
+ 7, 10, 7, 12, 4, 12, 4, 10,
+};
+
+uint16_t _first_poly[] = {
+ 0, 2, 4, 5, 6, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 32, 34, 35, 37, 38, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68,
+ 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 83, 85, 86, 87, 88, 89, 90,
+ 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 108,
+};
+
+uint16_t _num_polys[] = {
+ 2, 2, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 2, 1, 2, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 0,
+};
+
+uint16_t _first_coord[] = {
+ 0, 4, 8, 12, 16, 50, 94, 118, 122, 126, 172, 180, 200, 220, 248, 260,
+ 274, 278, 282, 310, 352, 356, 370, 400, 428, 455, 475, 501, 515, 552, 578, 582,
+ 586, 590, 604, 640, 644, 648, 684, 704, 708, 728, 751, 785, 805, 828, 840, 850,
+ 870, 882, 894, 906, 938, 944, 964, 984, 1002, 1019, 1043, 1066, 1102, 1110, 1122, 1138,
+ 1158, 1202, 1226, 1250, 1258, 1286, 1294, 1318, 1322, 1334, 1354, 1371, 1391, 1407, 1428, 1444,
+ 1464, 1476, 1480, 1488, 1492, 1500, 1524, 1532, 1550, 1560, 1578, 1595, 1611, 1621, 1641, 1657,
+ 1667, 1683, 1703, 1739, 1755, 1779, 1799, 1803, 1823, 1843, 1851, 1855,
+};
+
+uint16_t _num_coords[] = {
+ 4, 4, 4, 4, 34, 44, 24, 4, 4, 46, 8, 20, 20, 28, 12, 14, 4, 4, 28, 42, 4, 14,
+ 30, 28, 27, 20, 26, 14, 37, 26, 4, 4, 4, 14, 36, 4, 4, 36, 20, 4, 20, 23, 34, 20,
+ 23, 12, 10, 20, 12, 12, 12, 32, 6, 20, 20, 18, 17, 24, 23, 36, 8, 12, 16, 20, 44, 24,
+ 24, 8, 28, 8, 24, 4, 12, 20, 17, 20, 16, 21, 16, 20, 12, 4, 8, 4, 8, 24, 8, 18,
+ 10, 18, 17, 16, 10, 20, 16, 10, 16, 20, 36, 16, 24, 20, 4, 20, 20, 8, 4, 8,
+};
+
enum struct GdsiiRecord : uint8_t {
+ HEADER = 0X00,
+ BGNLIB = 0X01,
+ LIBNAME = 0X02,
+ UNITS = 0X03,
+ ENDLIB = 0X04,
+ BGNSTR = 0X05,
+ STRNAME = 0X06,
+ ENDSTR = 0X07,
+ BOUNDARY = 0X08,
+ PATH = 0X09,
+ SREF = 0X0A,
+ AREF = 0X0B,
+ TEXT = 0X0C,
+ LAYER = 0X0D,
+ DATATYPE = 0X0E,
+ WIDTH = 0X0F,
+ XY = 0X10,
+ ENDEL = 0X11,
+ SNAME = 0X12,
+ COLROW = 0X13,
+ TEXTNODE = 0X14,
+ NODE = 0X15,
+ TEXTTYPE = 0X16,
+ PRESENTATION = 0X17,
+ SPACING = 0X18,
+ STRING = 0X19,
+ STRANS = 0X1A,
+ MAG = 0X1B,
+ ANGLE = 0X1C,
+ UINTEGER = 0X1D,
+ USTRING = 0X1E,
+ REFLIBS = 0X1F,
+ FONTS = 0X20,
+ PATHTYPE = 0X21,
+ GENERATIONS = 0X22,
+ ATTRTABLE = 0X23,
+ STYPTABLE = 0X24,
+ STRTYPE = 0X25,
+ ELFLAGS = 0X26,
+ ELKEY = 0X27,
+ LINKTYPE = 0X28,
+ LINKKEYS = 0X29,
+ NODETYPE = 0X2A,
+ PROPATTR = 0X2B,
+ PROPVALUE = 0X2C,
+ BOX = 0X2D,
+ BOXTYPE = 0X2E,
+ PLEX = 0X2F,
+ BGNEXTN = 0X30,
+ ENDEXTN = 0X31,
+ TAPENUM = 0X32,
+ TAPECODE = 0X33,
+ STRCLASS = 0X34,
+ RESERVED = 0X35,
+ FORMAT = 0X36,
+ MASK = 0X37,
+ ENDMASKS = 0X38,
+ LIBDIRSIZE = 0X39,
+ SRFNAME = 0X3A,
+ LIBSECUR = 0X3B,
+ RAITHMBMSPATH = 0x5A,
+ RAITHPXXDATA = 0x62,
+};
+
+enum struct GdsiiDataType : uint8_t {
+ NoData = 0,
+ BitArray = 1,
+ TwoByteSignedInteger = 2,
+ FourByteSignedInteger = 3,
+ FourByteReal = 4,
+ EightByteReal = 5,
+ AsciiString = 6
+};
+
+uint64_t gdsii_real_from_double(double value);
+
+double gdsii_real_to_double(uint64_t real);
+
+// Read a record and swaps only first 2 bytes (record length). The size of the
+// buffer must be passed in buffer_count. On return, the record length
+// (including header) is returned in buffer_count.
+ErrorCode gdsii_read_record(FILE* in, uint8_t* buffer, uint64_t& buffer_count);
+
/*
+Copyright 2020 Lucas Heitzmann Gabrielli.
+This file is part of gdstk, distributed under the terms of the
+Boost Software License - Version 1.0. See the accompanying
+LICENSE file or <http://www.boost.org/LICENSE_1_0.txt>
+*/
+
+#ifndef GDSTK_HEADER_GDSTK
+#define GDSTK_HEADER_GDSTK
+
+#define __STDC_FORMAT_MACROS 1
+#define _USE_MATH_DEFINES
+
+#define GDSTK_VERSION "0.9.53"
+
+// If GDSTK_CUSTOM_ALLOCATOR is defined, the user must supply implementations
+// for the following dynamic memory management functions:
+// void* allocate(uint64_t size);
+// void* reallocate(void* ptr, uint64_t size);
+// void* allocate_clear(uint64_t size);
+// void free_allocation(void* ptr);
+// They will be used throughout the library instead of malloc, realloc, calloc
+// and free.
+//
+// #define GDSTK_CUSTOM_ALLOCATOR
+
+// After installation, this should be the only header required to be included
+// by the user. All other headers are included below.
+
+#include "array.hpp"
+#include "cell.hpp"
+#include "clipper_tools.hpp"
+#include "curve.hpp"
+#include "flexpath.hpp"
+#include "gdsii.hpp"
+#include "gdswriter.hpp"
+#include "label.hpp"
+#include "library.hpp"
+#include "map.hpp"
+#include "oasis.hpp"
+#include "pathcommon.hpp"
+#include "polygon.hpp"
+#include "raithdata.hpp"
+#include "rawcell.hpp"
+#include "reference.hpp"
+#include "repetition.hpp"
+#include "robustpath.hpp"
+#include "set.hpp"
+#include "sort.hpp"
+#include "style.hpp"
+#include "utils.hpp"
+#include "vec.hpp"
+
+#endif
+
// Struct used to write GDSII files incrementally, so that not all cells need
+// to be held in memory simultaneously. It should not be created manually, but
+// through gdswriter_init. Once created, use write_cell and write_rawcell to
+// output all layout cells as needed, then call close to finalize the GDSII
+// file.
+struct GdsWriter {
+ FILE* out;
+ double unit;
+ double precision;
+ uint64_t max_points;
+ tm timestamp;
+ // Used by the python interface to store the associated PyObject* (if any).
+ // No functions in gdstk namespace should touch this value!
+ void* owner;
+
+ ErrorCode write_cell(Cell& cell) const {
+ return cell.to_gds(out, unit / precision, max_points, precision, ×tamp);
+ }
+
+ ErrorCode write_rawcell(RawCell& rawcell) const { return rawcell.to_gds(out); }
+
+ void close() {
+ uint16_t buffer_end[] = {4, 0x0400};
+ big_endian_swap16(buffer_end, COUNT(buffer_end));
+ fwrite(buffer_end, sizeof(uint16_t), COUNT(buffer_end), out);
+ fclose(out);
+ }
+};
+
+// Create a GDSII file with the given filename for output and write the header
+// information to the file. If max_points > 4, polygons written from
+// write_cell will be fractured to max_points. GDSII files include a
+// timestamp, which can be specified by the caller or left NULL, in which case
+// the current time will be used. If not NULL, any errors will be reported
+// through error_code.
+inline GdsWriter gdswriter_init(const char* filename, const char* library_name, double unit,
+ double precision, uint64_t max_points, tm* timestamp,
+ ErrorCode* error_code) {
+ GdsWriter result = {NULL, unit, precision, max_points};
+
+ if (timestamp) {
+ result.timestamp = *timestamp;
+ } else {
+ get_now(result.timestamp);
+ }
+
+ result.out = fopen(filename, "wb");
+ if (result.out == NULL) {
+ fputs("[GDSTK] Unable to open GDSII file for output.\n", error_logger);
+ if (error_code) *error_code = ErrorCode::OutputFileOpenError;
+ return result;
+ }
+
+ uint64_t len = strlen(library_name);
+ if (len % 2) len++;
+ uint16_t buffer_start[] = {6,
+ 0x0002,
+ 0x0258,
+ 28,
+ 0x0102,
+ (uint16_t)(result.timestamp.tm_year + 1900),
+ (uint16_t)(result.timestamp.tm_mon + 1),
+ (uint16_t)result.timestamp.tm_mday,
+ (uint16_t)result.timestamp.tm_hour,
+ (uint16_t)result.timestamp.tm_min,
+ (uint16_t)result.timestamp.tm_sec,
+ (uint16_t)(result.timestamp.tm_year + 1900),
+ (uint16_t)(result.timestamp.tm_mon + 1),
+ (uint16_t)result.timestamp.tm_mday,
+ (uint16_t)result.timestamp.tm_hour,
+ (uint16_t)result.timestamp.tm_min,
+ (uint16_t)result.timestamp.tm_sec,
+ (uint16_t)(4 + len),
+ 0x0206};
+ big_endian_swap16(buffer_start, COUNT(buffer_start));
+ fwrite(buffer_start, sizeof(uint16_t), COUNT(buffer_start), result.out);
+ fwrite(library_name, 1, len, result.out);
+
+ uint16_t buffer_units[] = {20, 0x0305};
+ big_endian_swap16(buffer_units, COUNT(buffer_units));
+ fwrite(buffer_units, sizeof(uint16_t), COUNT(buffer_units), result.out);
+ uint64_t units[] = {gdsii_real_from_double(precision / unit),
+ gdsii_real_from_double(precision)};
+ big_endian_swap64(units, COUNT(units));
+ fwrite(units, sizeof(uint64_t), COUNT(units), result.out);
+ return result;
+}
+
enum struct Anchor { NW = 0, N = 1, NE = 2, W = 4, O = 5, E = 6, SW = 8, S = 9, SE = 10 };
+
+struct Label {
+ Tag tag;
+ char* text; // NULL-terminated text string
+ Vec2 origin;
+ Anchor anchor; // Text anchor (not supported by OASIS)
+ double rotation; // in radians (not supported by OASIS)
+ double magnification; // (not supported by OASIS)
+ bool x_reflection; // (not supported by OASIS)
+ Repetition repetition;
+ Property* properties;
+ // Used by the python interface to store the associated PyObject* (if any).
+ // No functions in gdstk namespace should touch this value!
+ void* owner;
+
+ void init(const char* text_) {
+ text = copy_string(text_, NULL);
+ magnification = 1.0;
+ }
+
+ void print();
+
+ void clear();
+
+ // This label instance must be zeroed before copy_from
+ void copy_from(const Label& label);
+
+ // Bounding box corners are returned in min and max. Repetitions are taken
+ // into account for the calculation.
+ void bounding_box(Vec2& min, Vec2& max) const;
+
+ // Transformations are applied in the order of arguments, starting with
+ // magnification and translating by origin at the end. This is equivalent
+ // to the transformation defined by a Reference with the same arguments.
+ void transform(double mag, bool x_refl, double rot, const Vec2 orig);
+
+ // Append the copies of this label defined by its repetition to result.
+ void apply_repetition(Array<Label*>& result);
+
+ // These functions output the label in the GDSII and SVG formats. They are
+ // not supposed to be called by the user.
+ ErrorCode to_gds(FILE* out, double scaling) const;
+ ErrorCode to_svg(FILE* out, double scaling, uint32_t precision) const;
+};
+
struct Library {
+ // NULL-terminated string with library name. The GDSII specification
+ // allows only ASCII-encoded strings. Gdstk does NOT enforce either rule.
+ // The name is not used in OASIS files.
+ char* name;
+
+ // Unit and precision used to define the library geometry. Please see the
+ // discussion about these values in the accompanying HTML documentation or
+ // at https://heitzmann.github.io/gdstk/gettingstarted.html
+ double unit;
+ double precision;
+
+ // Cells should be added to (or removed from) the library using these
+ // arrays. Each cell must have a unique name within the library, but Gdstk
+ // does NOT enforce it.
+ Array<Cell*> cell_array;
+ Array<RawCell*> rawcell_array;
+
+ Property* properties;
+
+ // Used by the python interface to store the associated PyObject* (if any).
+ // No functions in gdstk namespace should touch this value!
+ void* owner;
+
+ void init(const char* name_, double unit_, double precision_) {
+ name = copy_string(name_, NULL);
+ unit = unit_;
+ precision = precision_;
+ }
+
+ void print(bool all) const;
+
+ void clear() {
+ if (name) free_allocation(name);
+ name = NULL;
+ cell_array.clear();
+ rawcell_array.clear();
+ properties_clear(properties);
+ }
+
+ // Clear and free the memory of the whole library (this should be used with
+ // caution, it assumes all the memory is allocated dynamically and frees
+ // it). It does not touch rawcells in rawcell_array, but it frees all cell
+ // contents from cell_array.
+ void free_all() {
+ for (uint64_t i = 0; i < cell_array.count; i++) {
+ cell_array[i]->free_all();
+ free_allocation(cell_array[i]);
+ }
+ clear();
+ }
+
+ // This library instance must be zeroed before copy_from.
+ // If deep_copy == true, new cells are allocated and deep copied from the
+ // source. Otherwise, the same cell pointers are used.
+ void copy_from(const Library& library, bool deep_copy);
+
+ // Append all polygons/paths or labels tags found in this library's cells
+ // to result (rawcells are not included).
+ void get_shape_tags(Set<Tag>& result) const;
+ void get_label_tags(Set<Tag>& result) const;
+
+ // Append the top level cells (and raw cells) to the respective arrays.
+ // Top level cells are those that do not appear as dependencies of other
+ // cells in the library.
+ void top_level(Array<Cell*>& top_cells, Array<RawCell*>& top_rawcells) const;
+
+ // Find cell or rawcell by name. Return NULL if not found.
+ Cell* get_cell(const char* name) const;
+ RawCell* get_rawcell(const char* name) const;
+
+ // Rename a cell in the library, updating any references that use the old
+ // name with the new one. Note: these assume cell names are dynamically
+ // allocated in cells and references throughout the library.
+ void rename_cell(const char* old_name, const char* new_name);
+ void rename_cell(Cell* cell, const char* new_name);
+
+ // Replace a library cell updating references to the old cell with
+ // references to the new one. If old_cell is not present in the library,
+ // new_cell is not inserted either, but references are updated anyway.
+ void replace_cell(Cell* old_cell, Cell* new_cell);
+ void replace_cell(RawCell* old_cell, Cell* new_cell);
+ void replace_cell(Cell* old_cell, RawCell* new_cell);
+ void replace_cell(RawCell* old_cell, RawCell* new_cell);
+
+ // Change the tags of all elements in this library. Map keys are the
+ // current element tags and map values are the desired new tags.
+ void remap_tags(const TagMap& map) {
+ for (uint64_t i = 0; i < cell_array.count; i++) cell_array[i]->remap_tags(map);
+ };
+
+ // Output this library to a GDSII file. All polygons are fractured to
+ // max_points before saving (but the originals are kept) if max_points > 4.
+ // GDSII files include a timestamp, which can be specified by the caller or
+ // left NULL, in which case the current time will be used.
+ ErrorCode write_gds(const char* filename, uint64_t max_points, tm* timestamp) const;
+
+ // Output this library to an OASIS file. The OASIS specification includes
+ // support for a few special shapes, which can significantly decrease the
+ // file size. Circle detection is enabled by setting circle_tolerance > 0.
+ // Rectangles and trapezoids are enabled via config_flags. Further size
+ // reduction can be achieved by setting deflate_level > 0 (up to 9, for
+ // maximal compression). Finally, config_flags is a bit-field value
+ // obtained by or-ing OASIS_CONFIG_* constants, defined in oasis.h
+ ErrorCode write_oas(const char* filename, double circle_tolerance, uint8_t deflate_level,
+ uint16_t config_flags);
+};
+
+// Struct used to get information from a library file without loading the
+// complete library.
+struct LibraryInfo {
+ Array<char*> cell_names;
+ Set<Tag> shape_tags;
+ Set<Tag> label_tags;
+ uint64_t num_polygons;
+ uint64_t num_paths;
+ uint64_t num_references;
+ uint64_t num_labels;
+ double unit;
+ double precision;
+
+ void clear() {
+ for (uint64_t i = 0; i < cell_names.count; i++) {
+ free_allocation(cell_names[i]);
+ cell_names[i] = NULL;
+ }
+ cell_names.clear();
+ shape_tags.clear();
+ label_tags.clear();
+ num_polygons = 0;
+ num_paths = 0;
+ num_references = 0;
+ num_labels = 0;
+ unit = 0;
+ precision = 0;
+ }
+};
+
+// Read the contents of a GDSII file into a new library. If unit is not zero,
+// the units in the file are converted (all elements are properly scaled to the
+// desired unit). The value of tolerance is used as the default tolerance for
+// paths in the library. If shape_tags is not empty, only shapes in those
+// tags will be imported. If not NULL, any errors will be reported through
+// error_code.
+Library read_gds(const char* filename, double unit, double tolerance, const Set<Tag>* shape_tags,
+ ErrorCode* error_code);
+
+// Read the contents of an OASIS file into a new library. If unit is not zero,
+// the units in the file are converted (all elements are properly scaled to the
+// desired unit). The value of tolerance is used as the default tolerance for
+// paths in the library and for the creation of circles. If shape_tags is not
+// empty, only shapes in those tags will be imported. If not NULL, any errors
+// will be reported through error_code.
+Library read_oas(const char* filename, double unit,
+ double tolerance, // TODO: const Set<Tag>* shape_tags,
+ ErrorCode* error_code);
+
+// Read the unit and precision of a GDSII file and return in the respective
+// arguments.
+ErrorCode gds_units(const char* filename, double& unit, double& precision);
+
+// Get/set the timestamps in the GDSII file. If timestamp is not NULL, set the
+// timestamps of the file. The main library timestamp before any modification
+// is returned.
+tm gds_timestamp(const char* filename, const tm* new_timestamp, ErrorCode* error_code);
+
+// Gather information about the GDSII file. Return argument info must be
+// properly initialized.
+ErrorCode gds_info(const char* filename, LibraryInfo& info);
+
+// Read the precision of an OASIS file (unit is always 1e-6) and return in the
+// precision argument.
+ErrorCode oas_precision(const char* filename, double& precision);
+
+// Return true if the file signature checks or if the file has no validation
+// data. If signature is provided, the calculated signature is stored there.
+// If not NULL, any errors will be reported through error_code. If the file
+// has no checksum data, signature will be set to zero and error_code to
+// ErrorCode::ChecksumError if they are not NULL.
+bool oas_validate(const char* filename, uint32_t* signature, ErrorCode* error_code);
+
+// TODO: Gather information about file
+// ErrorCode oas_info(const char* filename, LibraryInfo& info);
+
template <class T>
+struct MapItem {
+ char* key;
+ T value;
+};
+
+// Hash map indexed by NULL-terminated strings
+template <class T>
+struct Map {
+ uint64_t capacity; // allocated capacity
+ uint64_t count; // number of items in the map
+ MapItem<T>* items; // array with length capacity
+
+ void print(bool all, void (*value_print)(const T&)) const {
+ printf("Map <%p>, count %" PRIu64 "/%" PRIu64 ", items <%p>\n", this, count, capacity,
+ items);
+ if (all) {
+ MapItem<T>* item = items;
+ if (value_print) {
+ for (uint64_t i = 0; i < capacity; i++, item++) {
+ printf("Item %" PRIu64 " <%p>, key %p (%s): ", i, item, item->key,
+ item->key ? item->key : "");
+ value_print(item->value);
+ }
+ } else {
+ for (uint64_t i = 0; i < capacity; i++, item++) {
+ printf("Item %" PRIu64 " <%p>, key %p (%s): value <%p>\n", i, item, item->key,
+ item->key ? item->key : "", item->value);
+ }
+ }
+ }
+ }
+
+ // The instance should be zeroed before using copy_from
+ void copy_from(const Map<T>& map) {
+ count = 0;
+ capacity = map.capacity;
+ items = (MapItem<T>*)allocate_clear(capacity * sizeof(MapItem<T>));
+ for (MapItem<T>* item = map.next(NULL); item; item = map.next(item)) {
+ set(item->key, item->value);
+ }
+ }
+
+ void resize(uint64_t new_capacity) {
+ Map<T> new_map;
+ new_map.count = 0;
+ new_map.capacity = new_capacity;
+ new_map.items = (MapItem<T>*)allocate_clear(new_capacity * sizeof(MapItem<T>));
+ const MapItem<T>* limit = items + capacity;
+ for (MapItem<T>* it = items; it != limit; it++) {
+ if (it->key) new_map.set(it->key, it->value);
+ }
+ clear();
+ capacity = new_map.capacity;
+ count = new_map.count;
+ items = new_map.items;
+ }
+
+ // Function to iterate over all values in the map:
+ // for (MapItem<T>* item = map.next(NULL); item; item = map.next(item)) {…}
+ MapItem<T>* next(const MapItem<T>* current) const {
+ MapItem<T>* next_ = current ? (MapItem<T>*)(current + 1) : items;
+ const MapItem<T>* limit = items + capacity;
+ while (next_ < limit) {
+ if (next_->key) return next_;
+ next_++;
+ }
+ return NULL;
+ }
+
+ void to_array(Array<T>& result) const {
+ result.ensure_slots(count);
+ const MapItem<T>* limit = items + capacity;
+ for (MapItem<T>* it = items; it != limit; it++) {
+ if (it->key) result.append_unsafe(it->value);
+ }
+ }
+
+ void clear() {
+ if (items) {
+ MapItem<T>* item = items;
+ for (uint64_t i = 0; i < capacity; i++, item++) {
+ if (item->key) {
+ free_allocation(item->key);
+ item->key = NULL;
+ }
+ }
+ free_allocation(items);
+ items = NULL;
+ }
+ capacity = 0;
+ count = 0;
+ }
+
+ MapItem<T>* get_slot(const char* key) const {
+ assert(capacity > 0);
+ assert(count < capacity);
+ uint64_t h = hash(key) % capacity;
+ MapItem<T>* item = items + h;
+ while (item->key != NULL && strcmp(item->key, key) != 0) {
+ item++;
+ if (item == items + capacity) item = items;
+ }
+ // DEBUG_PRINT("get_slot %s [%" PRIu64 "] -> [%" PRIu64 "]\n", key, h, item - items);
+ return item;
+ }
+
+ // Key is internally allocated and copied; value is simply assigned
+ void set(const char* key, T value) {
+ // Equality is important for capacity == 0
+ if (count * 10 >= capacity * GDSTK_MAP_CAPACITY_THRESHOLD)
+ resize(capacity >= GDSTK_INITIAL_MAP_CAPACITY ? capacity * GDSTK_MAP_GROWTH_FACTOR
+ : GDSTK_INITIAL_MAP_CAPACITY);
+ MapItem<T>* item = get_slot(key);
+ if (item->key == NULL) {
+ item->key = copy_string(key, NULL);
+ count++;
+ }
+ item->value = value;
+ }
+
+ bool has_key(const char* key) const {
+ if (count == 0) return false;
+ const MapItem<T>* item = get_slot(key);
+ return item->key != NULL;
+ }
+
+ // If the desired key is not found, returns T{}
+ T get(const char* key) const {
+ if (count == 0) return T{};
+ const MapItem<T>* item = get_slot(key);
+ return item->key == NULL ? T{} : item->value;
+ }
+
+ // Return true if the key existed, false otherwise
+ bool del(const char* key) {
+ if (count == 0) return false;
+ MapItem<T>* item = get_slot(key);
+ if (item->key == NULL) return false;
+
+ // DEBUG_PRINT("DEL [%" PRIu64 "] %s\n", item - items, item->key);
+ free_allocation(item->key);
+ item->key = NULL;
+ count--;
+
+ // Re-insert this block to fill any undesired gaps
+ while (true) {
+ item++;
+ if (item == items + capacity) item = items;
+ if (item->key == NULL) return true;
+ char* temp_key = item->key;
+ item->key = NULL;
+ MapItem<T>* new_item = get_slot(temp_key);
+ new_item->key = temp_key;
+ new_item->value = item->value;
+ // if (new_item != item) {
+ // DEBUG_PRINT("MOVE %s [%" PRIu64 "] -> [%" PRIu64 "]\n", new_item->key,
+ // item - items, new_item - items);
+ // }
+ }
+ assert(false);
+ return true;
+ }
+};
+
// Configuration flags for Library.write_oas
+#define OASIS_CONFIG_PROPERTY_MAX_COUNTS 0x0001
+#define OASIS_CONFIG_PROPERTY_TOP_LEVEL 0x0002
+#define OASIS_CONFIG_PROPERTY_BOUNDING_BOX 0x0004
+#define OASIS_CONFIG_PROPERTY_CELL_OFFSET 0x0008
+
+#define OASIS_CONFIG_DETECT_RECTANGLES 0x0010
+#define OASIS_CONFIG_DETECT_TRAPEZOIDS 0x0020
+
+#define OASIS_CONFIG_INCLUDE_CRC32 0x0040
+#define OASIS_CONFIG_INCLUDE_CHECKSUM32 0x0080
+
+#define OASIS_CONFIG_STANDARD_PROPERTIES \
+ (OASIS_CONFIG_PROPERTY_MAX_COUNTS | OASIS_CONFIG_PROPERTY_TOP_LEVEL | \
+ OASIS_CONFIG_PROPERTY_BOUNDING_BOX | OASIS_CONFIG_PROPERTY_CELL_OFFSET)
+#define OASIS_CONFIG_DETECT_ALL (OASIS_CONFIG_DETECT_RECTANGLES | OASIS_CONFIG_DETECT_TRAPEZOIDS)
+
+// Types and functions in this header are not meant to be used by the end user
+// of the library. They are for internal use only.
+
+extern const char s_max_int_size_property_name[];
+extern const char s_max_uint_size_property_name[];
+extern const char s_max_string_size_property_name[];
+extern const char s_max_polygon_property_name[];
+extern const char s_max_path_property_name[];
+extern const char s_top_level_property_name[];
+extern const char s_bounding_box_available_property_name[];
+extern const char s_bounding_box_property_name[];
+extern const char s_cell_offset_property_name[];
+extern const char s_gds_property_name[];
+
+enum struct OasisDataType : uint8_t {
+ RealPositiveInteger = 0,
+ RealNegativeInteger = 1,
+ RealPositiveReciprocal = 2,
+ RealNegativeReciprocal = 3,
+ RealPositiveRatio = 4,
+ RealNegativeRatio = 5,
+ RealFloat = 6,
+ RealDouble = 7,
+ UnsignedInteger = 8,
+ SignedInteger = 9,
+ AString = 10, // printable characters + space (0x20 through 0x7e)
+ BString = 11, // any bytes
+ NString = 12, // printable characters (0x21 through 0x7e), count > 0
+ ReferenceA = 13,
+ ReferenceB = 14,
+ ReferenceN = 15
+};
+
+enum struct OasisRepetition : uint8_t {
+ Previous = 0,
+ Rectangular = 1,
+ RectangularX = 2,
+ RectangularY = 3,
+ ExplicitX = 4,
+ ExplicitXGrid = 5,
+ ExplicitY = 6,
+ ExplicitYGrid = 7,
+ Regular = 8,
+ Linear = 9,
+ Explicit = 10,
+ ExplicitGrid = 11
+};
+
+enum struct OasisPointList : uint8_t {
+ ManhattanHorizontalFirst = 0,
+ ManhattanVerticalFirst = 1,
+ Manhattan = 2,
+ Octangular = 3,
+ General = 4,
+ Relative = 5
+};
+
+enum struct OasisValidation : uint8_t { None = 0, Crc32 = 1, CheckSum = 2 };
+
+enum struct OasisInterval : uint8_t {
+ AllValues = 0,
+ UpperBound = 1,
+ LowerBound = 2,
+ SingleValue = 3,
+ Bounded = 4
+};
+
+enum struct OasisDirection : uint8_t { E = 0, N = 1, W = 2, S = 3, NE = 4, NW = 5, SW = 6, SE = 7 };
+
+enum struct OasisRecord : uint8_t {
+ PAD = 0,
+ START = 1,
+ END = 2,
+ CELLNAME_IMPLICIT = 3,
+ CELLNAME = 4,
+ TEXTSTRING_IMPLICIT = 5,
+ TEXTSTRING = 6,
+ PROPNAME_IMPLICIT = 7,
+ PROPNAME = 8,
+ PROPSTRING_IMPLICIT = 9,
+ PROPSTRING = 10,
+ LAYERNAME_DATA = 11,
+ LAYERNAME_TEXT = 12,
+ CELL_REF_NUM = 13,
+ CELL = 14,
+ XYABSOLUTE = 15,
+ XYRELATIVE = 16,
+ PLACEMENT = 17,
+ PLACEMENT_TRANSFORM = 18,
+ TEXT = 19,
+ RECTANGLE = 20,
+ POLYGON = 21,
+ PATH = 22,
+ TRAPEZOID_AB = 23,
+ TRAPEZOID_A = 24,
+ TRAPEZOID_B = 25,
+ CTRAPEZOID = 26,
+ CIRCLE = 27,
+ PROPERTY = 28,
+ LAST_PROPERTY = 29,
+ XNAME_IMPLICIT = 30,
+ XNAME = 31,
+ XELEMENT = 32,
+ XGEOMETRY = 33,
+ CBLOCK = 34
+};
+
+struct OasisStream {
+ FILE* file;
+ uint8_t* data;
+ uint8_t* cursor;
+ uint64_t data_size;
+ uint32_t signature;
+ bool crc32;
+ bool checksum32;
+ ErrorCode error_code;
+};
+
+struct OasisState {
+ double scaling;
+ double circle_tolerance;
+ Map<uint64_t> property_name_map;
+ Array<PropertyValue*> property_value_array;
+ uint16_t config_flags;
+};
+
+ErrorCode oasis_read(void* buffer, size_t size, size_t count, OasisStream& in);
+
+size_t oasis_write(const void* buffer, size_t size, size_t count, OasisStream& out);
+
+int oasis_putc(int c, OasisStream& out);
+
+uint8_t* oasis_read_string(OasisStream& in, bool append_terminating_null, uint64_t& len);
+
+uint64_t oasis_read_unsigned_integer(OasisStream& in);
+
+int64_t oasis_read_integer(OasisStream& in);
+
+inline int64_t oasis_read_1delta(OasisStream& in) { return oasis_read_integer(in); };
+
+void oasis_read_2delta(OasisStream& in, int64_t& x, int64_t& y);
+
+void oasis_read_3delta(OasisStream& in, int64_t& x, int64_t& y);
+
+void oasis_read_gdelta(OasisStream& in, int64_t& x, int64_t& y);
+
+double oasis_read_real_by_type(OasisStream& in, OasisDataType type);
+
+inline double oasis_read_real(OasisStream& in) {
+ OasisDataType type;
+ if (oasis_read(&type, 1, 1, in) != ErrorCode::NoError) return 0;
+ return oasis_read_real_by_type(in, type);
+}
+
+// result must have at least 1 point in it, which will be used as reference for the relative deltas.
+// polygon indicates whether this is supposed to be a polygon point list (in which case there will
+// be an implicit extra delta for Manhattan types).
+uint64_t oasis_read_point_list(OasisStream& in, double scaling, bool closed, Array<Vec2>& result);
+
+void oasis_read_repetition(OasisStream& in, double scaling, Repetition& repetition);
+
+void oasis_write_unsigned_integer(OasisStream& out, uint64_t value);
+
+void oasis_write_integer(OasisStream& out, int64_t value);
+
+inline void oasis_write_1delta(OasisStream& out, int64_t value) {
+ oasis_write_integer(out, value);
+};
+
+void oasis_write_2delta(OasisStream& out, int64_t x, int64_t y);
+
+void oasis_write_3delta(OasisStream& out, int64_t x, int64_t y);
+
+void oasis_write_gdelta(OasisStream& out, int64_t x, int64_t y);
+
+void oasis_write_real(OasisStream& out, double value);
+
+// Uses first point as reference, does not output it. Deltas between neighboring points
+// will be calculated in-place: points[i] = points[i] - points[i - 1] (i = 1...count - 1)
+void oasis_write_point_list(OasisStream& out, Array<IntVec2>& points, bool closed);
+// Uses first point as reference, does not output it.
+void oasis_write_point_list(OasisStream& out, const Array<Vec2> points, double scaling,
+ bool closed);
+
+// This should only be called with repetition.get_count() > 1
+void oasis_write_repetition(OasisStream& out, const Repetition repetition, double scaling);
+
enum struct EndType {
+ Flush = 0,
+ Round,
+ HalfWidth,
+ Extended, // Use end_extensions
+ Smooth, // Becomes Round if simple_path
+ Function, // Use end_function(…)
+};
+
+enum struct JoinType {
+ Natural = 0, // Only bevel acute joins
+ Miter,
+ Bevel,
+ Round,
+ Smooth, // Becomes Round if simple_path
+ Function, // Use join_function(…)
+};
+
+enum struct BendType {
+ None = 0,
+ Circular, // Use bend_radius
+ Function, // Use bend_function(…)
+};
+
+inline const char* end_type_name(EndType end_type) {
+ switch (end_type) {
+ case EndType::Flush:
+ return "flush";
+ case EndType::Round:
+ return "round";
+ case EndType::HalfWidth:
+ return "half-width";
+ case EndType::Extended:
+ return "extended";
+ case EndType::Smooth:
+ return "smooth";
+ case EndType::Function:
+ return "function";
+ }
+ return "unknown";
+}
+
+inline const char* join_type_name(JoinType join_type) {
+ switch (join_type) {
+ case JoinType::Natural:
+ return "natural";
+ case JoinType::Miter:
+ return "miter";
+ case JoinType::Bevel:
+ return "bevel";
+ case JoinType::Round:
+ return "round";
+ case JoinType::Smooth:
+ return "smooth";
+ case JoinType::Function:
+ return "function";
+ }
+ return "unknown";
+}
+
+inline const char* bend_type_name(BendType bend_type) {
+ switch (bend_type) {
+ case BendType::None:
+ return "none";
+ case BendType::Circular:
+ return "circular";
+ case BendType::Function:
+ return "function";
+ }
+ return "unknown";
+}
+
struct Polygon {
+ Tag tag;
+ Array<Vec2> point_array;
+ Repetition repetition;
+ Property* properties;
+ // Used by the python interface to store the associated PyObject* (if any).
+ // No functions in gdstk namespace should touch this value!
+ void* owner;
+
+ void print(bool all) const;
+
+ void clear();
+
+ // This polygon instance must be zeroed before copy_from
+ void copy_from(const Polygon& polygon);
+
+ // Total polygon area including any repetitions
+ double area() const;
+
+ // Polygon area excluding repetitions with sign indicating orientation
+ // (positive for counter clockwise)
+ double signed_area() const;
+
+ // Total polygon perimeter including any repetitions
+ double perimeter() const;
+
+ // Check if the points are inside this polygon (points lying on the edges
+ // or coinciding with a vertex of the polygon are considered inside).
+ bool contain(const Vec2 point) const;
+ bool contain_all(const Array<Vec2>& points) const;
+ bool contain_any(const Array<Vec2>& points) const;
+
+ // Bounding box corners are returned in min and max. If the polygons has
+ // no vertices, return min.x > max.x. Repetitions are taken into account
+ // for the calculation.
+ void bounding_box(Vec2& min, Vec2& max) const;
+
+ void translate(const Vec2 v);
+ void scale(const Vec2 scale, const Vec2 center);
+ void mirror(const Vec2 p0, const Vec2 p1);
+ void rotate(double angle, const Vec2 center);
+
+ // Transformations are applied in the order of arguments, starting with
+ // magnification and translating by origin at the end. This is equivalent
+ // to the transformation defined by a Reference with the same arguments.
+ void transform(double magnification, bool x_reflection, double rotation, const Vec2 origin);
+
+ // Round the corners of this polygon. Argument radii can include one
+ // radius value for each polygon corner or less, in which case it will be
+ // cycled. If the desired fillet radius for a given corner is larger than
+ // half the shortest edge adjacent to that corner, it is reduced to that
+ // size. The number of vertices used to approximate the circular arcs is
+ // defined by tolerance.
+ void fillet(const Array<double> radii, double tolerance);
+
+ // Fracture the polygon horizontally and vertically until all pieces have
+ // at most max_points vertices. If max_points < 5, it doesn't do anything.
+ // Resulting pieces are appended to result.
+ void fracture(uint64_t max_points, double precision, Array<Polygon*>& result) const;
+
+ // Append the copies of this polygon defined by its repetition to result.
+ void apply_repetition(Array<Polygon*>& result);
+
+ // These functions output the polygon in the GDSII, OASIS and SVG formats.
+ // They are not supposed to be called by the user.
+ ErrorCode to_gds(FILE* out, double scaling) const;
+ ErrorCode to_oas(OasisStream& out, OasisState& state) const;
+ ErrorCode to_svg(FILE* out, double scaling, uint32_t precision) const;
+};
+
+Polygon rectangle(const Vec2 corner1, const Vec2 corner2, Tag tag);
+
+Polygon cross(const Vec2 center, double full_size, double arm_width, Tag tag);
+
+// The polygon is created with a horizontal lower edge when rotation is 0.
+Polygon regular_polygon(const Vec2 center, double side_length, uint64_t sides, double rotation,
+ Tag tag);
+
+// Create circles, ellipses, rings, or sections of those. The number of points
+// used to approximate the arcs is such that the approximation error is less
+// than tolerance.
+Polygon ellipse(const Vec2 center, double radius_x, double radius_y, double inner_radius_x,
+ double inner_radius_y, double initial_angle, double final_angle, double tolerance,
+ Tag tag);
+
+Polygon racetrack(const Vec2 center, double straight_length, double radius, double inner_radius,
+ bool vertical, double tolerance, Tag tag);
+
+// Create a polygonal text form NULL-terminated string s. Argument size
+// defines the full height of the glyphs. Polygons are appended to result.
+// The character aspect ratio is 1:2. For horizontal text, spacings between
+// characters and between lines are 9/16 and 5/4 times the full height size,
+// respectively. For vertical text, characters and columns are respectively
+// spaced by 9/8 and 1 times size.
+void text(const char* s, double size, const Vec2 position, bool vertical, Tag tag,
+ Array<Polygon*>& result);
+
+// Create polyogns based on the 2-d array data (in row-major orderr, with rows
+// * cols elements) by drawing the isolines at level. Scaling is used in the
+// boolean composition of resulting shapes to connect any holes and set the
+// overall precision. Resulting polygons are appended to result. Their length
+// scale is one data element, i.e., the data array has size cols × rows.
+ErrorCode contour(const double* data, uint64_t rows, uint64_t cols, double level, double scaling,
+ Array<Polygon*>& result);
+
+// Check if the points are inside a set of polygons (points lying on the edges
+// or coinciding with a vertex of the polygons are considered inside). Result
+// must be an array with size for at least points.count bools.
+void inside(const Array<Vec2>& points, const Array<Polygon*>& polygons, bool* result);
+bool all_inside(const Array<Vec2>& points, const Array<Polygon*>& polygons);
+bool any_inside(const Array<Vec2>& points, const Array<Polygon*>& polygons);
+
struct OasisStream;
+struct OasisState;
+
+// Properties (and their members) are assumed to always be allocated through
+// allocate, allocate_clear, or reallocate.
+
+enum struct PropertyType { UnsignedInteger, Integer, Real, String };
+
+// Each property can hold a series of values, which are represented by a
+// NULL-terminated linked list of PropertyValue
+struct PropertyValue {
+ PropertyType type;
+ union {
+ uint64_t unsigned_integer;
+ int64_t integer;
+ double real;
+ struct {
+ uint64_t count;
+ uint8_t* bytes;
+ };
+ };
+ PropertyValue* next;
+};
+
+// Properties are stored as a NULL-terminated linked list. Their name is a
+// NULL-terminated string. Property names in the OASIS should be strings of
+// characters within the range 0x21 and 0x7E with at least 1 character.
+struct Property {
+ char* name;
+ PropertyValue* value;
+ Property* next;
+};
+
+void properties_print(Property* properties);
+void properties_clear(Property*& properties);
+Property* properties_copy(const Property* properties);
+
+// property_values_copy is used in the OASIS reader; it is not intended to be
+// used elsewhere.
+PropertyValue* property_values_copy(const PropertyValue* values);
+
+// The set_property functions add values to the first existing property with
+// the given name. A new one is created if none exists or create_new == true.
+// New values are added to the start of the value list, so in the OASIS file,
+// they will appear in reverse of the insertion order (last added will appear
+// first). The NULL byte at the end of string is NOT included in the property
+// value.
+void set_property(Property*& properties, const char* name, uint64_t unsigned_integer,
+ bool create_new);
+void set_property(Property*& properties, const char* name, int64_t integer, bool create_new);
+void set_property(Property*& properties, const char* name, double real, bool create_new);
+void set_property(Property*& properties, const char* name, const char* string, bool create_new);
+void set_property(Property*& properties, const char* name, const uint8_t* bytes, uint64_t count,
+ bool create_new);
+
+// Overwrite properties with the same attribute number. The NULL byte is
+// included in the property value.
+void set_gds_property(Property*& properties, uint16_t attribute, const char* value);
+
+uint64_t remove_property(Property*& properties, const char* name, bool all_occurences);
+bool remove_gds_property(Property*& properties, uint16_t attribute);
+
+PropertyValue* get_property(Property* properties, const char* name);
+PropertyValue* get_gds_property(Property* properties, uint16_t attribute);
+
+// These functions output the properties in the GDSII and OASIS formats. They
+// are not supposed to be called by the user.
+ErrorCode properties_to_gds(const Property* properties, FILE* out);
+ErrorCode properties_to_oas(const Property* properties, OasisStream& out, OasisState& state);
+
struct RawSource {
+ FILE* file;
+ uint32_t uses;
+
+ // Read num_bytes into buffer from fd starting at offset
+ int64_t offset_read(void* buffer, uint64_t num_bytes, uint64_t offset) const {
+#ifdef _WIN32
+ // The POSIX version (pread) does not change the file cursor, this
+ // does. Furthermore, this is not thread-safe!
+ FSEEK64(file, offset, SEEK_SET);
+ return fread(buffer, 1, num_bytes, file);
+#else
+ return pread(fileno(file), buffer, num_bytes, offset);
+#endif
+ };
+};
+
+// Rawcells are not meant to be created except through read_rawcells. They are
+// not explicitly loaded in memory until used, so the GDSII file where they are
+// loaded from, remains open until it is no longer needed. That is done by
+// reference counting.
+struct RawCell {
+ char* name;
+ RawSource* source;
+ union {
+ uint8_t* data;
+ uint64_t offset;
+ };
+ uint64_t size;
+ Array<RawCell*> dependencies;
+ // Used by the python interface to store the associated PyObject* (if any).
+ // No functions in gdstk namespace should touch this value!
+ void* owner;
+
+ void print(bool all) const;
+
+ void clear();
+
+ // Append dependencies of this cell to result. If recursive is true, also
+ // includes dependencies of any dependencies recursively.
+ void get_dependencies(bool recursive, Map<RawCell*>& result) const;
+
+ // This function outputs the rawcell in the GDSII. It is not supposed to
+ // be called by the user.
+ ErrorCode to_gds(FILE* out);
+};
+
+// Load a GDSII file and extract its cells as RawCell.
+Map<RawCell*> read_rawcells(const char* filename, ErrorCode* error_code);
+
struct Cell;
+struct RawCell;
+struct GeometryInfo;
+
+enum struct ReferenceType { Cell = 0, RawCell, Name };
+
+struct Reference {
+ ReferenceType type;
+ // References by name or rawcell are limited in their use. Most cell
+ // functions will not work on them.
+ union {
+ Cell* cell;
+ RawCell* rawcell;
+ char* name;
+ };
+ Vec2 origin;
+ double rotation; // in radians
+ double magnification;
+ bool x_reflection;
+ Repetition repetition;
+ Property* properties;
+ // Used by the python interface to store the associated PyObject* (if any).
+ // No functions in gdstk namespace should touch this value!
+ void* owner;
+
+ void init(Cell* cell_) {
+ type = ReferenceType::Cell;
+ cell = cell_;
+ magnification = 1.0;
+ }
+
+ void init(RawCell* rawcell_) {
+ type = ReferenceType::RawCell;
+ rawcell = rawcell_;
+ magnification = 1.0;
+ }
+
+ void init(const char* name_) {
+ type = ReferenceType::Name;
+ name = copy_string(name_, NULL);
+ magnification = 1.0;
+ }
+
+ void print() const;
+
+ void clear();
+
+ // This reference instance must be zeroed before copy_from
+ void copy_from(const Reference& reference);
+
+ // Calculate the bounding box of this reference and return the lower left
+ // and upper right corners in min and max, respectively. If the bounding
+ // box cannot be calculated, return min.x > max.x. The cached version can
+ // be used to retain the cache for latter use, but it is the user
+ // responsibility to invalidate the cache if any geometry changes.
+ void bounding_box(Vec2& min, Vec2& max) const;
+ void bounding_box(Vec2& min, Vec2& max, Map<GeometryInfo>& cache) const;
+
+ // Append the convex hull of this reference to result. The cached version
+ // follows the same rules explained for bounding_box.
+ void convex_hull(Array<Vec2>& result) const;
+ void convex_hull(Array<Vec2>& result, Map<GeometryInfo>& cache) const;
+
+ // Transformations are applied in the order of arguments, starting with
+ // magnification and translating by origin at the end. This is equivalent
+ // to the transformation defined by a Reference with the same arguments.
+ void transform(double mag, bool x_refl, double rot, const Vec2 orig);
+
+ // Append the copies of this reference defined by its repetition to result.
+ void apply_repetition(Array<Reference*>& result);
+
+ // Applies the transformation and repetition defined by this reference to
+ // the points in point_array, appending the results to the same array.
+ void repeat_and_transform(Array<Vec2>& point_array) const;
+
+ // These functions create and append the elements that are created by this
+ // reference to the result array. Argument depth controls how many levels
+ // of references should be included (references of references); if it is
+ // negative, all levels are included. If include_paths is true, the
+ // polygonal representation of paths are also included in polygons. If
+ // filter is true, only polygons in the indicated layer and data type are
+ // created.
+ void get_polygons(bool apply_repetitions, bool include_paths, int64_t depth, bool filter,
+ Tag tag, Array<Polygon*>& result) const;
+ void get_flexpaths(bool apply_repetitions, int64_t depth, bool filter, Tag tag,
+ Array<FlexPath*>& result) const;
+ void get_robustpaths(bool apply_repetitions, int64_t depth, bool filter, Tag tag,
+ Array<RobustPath*>& result) const;
+ void get_labels(bool apply_repetitions, int64_t depth, bool filter, Tag tag,
+ Array<Label*>& result) const;
+
+ // These functions output the reference in the GDSII and SVG formats. They
+ // are not supposed to be called by the user.
+ ErrorCode to_gds(FILE* out, double scaling) const;
+ ErrorCode to_svg(FILE* out, double scaling, uint32_t precision) const;
+};
+
enum struct RepetitionType {
+ None = 0, // No repetition
+ Rectangular, // columns × rows matrix of copies spaced by spacing
+ Regular, // columns × rows matrix along directions v1 and v2
+ Explicit, // explicit repetition at offsets
+ ExplicitX, // explicit repetitions along the x axis at coords
+ ExplicitY, // explicit repetitions along the y axis at coords
+};
+
+struct Repetition {
+ RepetitionType type;
+ union {
+ struct { // Rectangular and Regular
+ uint64_t columns; // Along x or v1
+ uint64_t rows; // Along y or v2
+ union {
+ Vec2 spacing; // Rectangular spacing
+ struct {
+ Vec2 v1; // Regular axis 1
+ Vec2 v2; // Regular axis 2
+ };
+ };
+ };
+ // The original element should not be explicitly included, i.e., (0, 0)
+ Array<Vec2> offsets; // Explicit
+ Array<double> coords; // ExplicitX and ExplicitY
+ };
+
+ void print() const;
+
+ void clear();
+
+ // This repetition instance must be zeroed before copy_from
+ void copy_from(const Repetition repetition);
+
+ // Return the number of repetitions created by this object, including the
+ // original
+ uint64_t get_count() const;
+
+ // Append the offsets generated by this repetition, including the original,
+ // (0, 0), as first element, to result.
+ void get_offsets(Array<Vec2>& result) const;
+
+ // Append the extrema offsets generated by this repetition, including the
+ // original, to result.
+ void get_extrema(Array<Vec2>& result) const;
+
+ // Transformations are applied in the order of arguments, starting with
+ // magnification and rotating at the end. This is equivalent to the
+ // transformation defined by a Reference with the same arguments.
+ void transform(double magnification, bool x_reflection, double rotation);
+};
+
// Robustpaths are similar to flexpaths in that they only hold a spine, that
+// represents the main shape of the desired path, plus any number of elements
+// that add widths and offsets on top of that spine to build the actual paths.
+// The main difference from flexpaths is that, unlike those, the spine for
+// robustpaths is represented by an array of parametric curves (subpaths),
+// instead of an open polygonal curve. Similarly, width and offset changes
+// along the path are represented parametrically by interpolation functions.
+// These properties make the process of finding the polygonal representation of
+// robustpaths more numerically robust, but also more computationally
+// intensive.
+
+enum struct InterpolationType {
+ Constant = 0, // Step-change in join region
+ Linear, // LERP from past value to new
+ Smooth, // SERP from past value to new
+ Parametric // Uses function(…)
+};
+
+struct Interpolation {
+ InterpolationType type;
+ union {
+ double value; // Constant
+ struct { // Linear or smooth interpolation
+ double initial_value;
+ double final_value;
+ };
+ struct {
+ ParametricDouble function; // Parametric
+ void* data; // User data
+ };
+ };
+};
+
+enum struct SubPathType {
+ Segment, // straight line segment
+ Arc, // elliptical arc
+ Bezier, // general Bézier
+ Bezier2, // quadratic Bézier
+ Bezier3, // cubic Bézier
+ Parametric // general parametric function
+};
+
+// Subpaths are not supposed to be created directly by the user, but through
+// the construction functions of the RobustPath class instead.
+struct SubPath {
+ SubPathType type;
+ union {
+ struct { // Segment
+ Vec2 begin;
+ Vec2 end;
+ };
+ struct { // Arc
+ // x = path.radius_x * cos(path.angle_i);
+ // y = path.radius_y * sin(path.angle_i);
+ // center = arc_start - Vec2{x * cos_rot - y * sin_rot, x * sin_rot + y * cos_rot};
+ Vec2 center;
+ double radius_x;
+ double radius_y;
+ double angle_i; // initial_angle - rotation;
+ double angle_f; // final_angle - rotation;
+ double cos_rot;
+ double sin_rot;
+ };
+ struct { // Bezier2, Bezier3
+ Vec2 p0;
+ Vec2 p1;
+ Vec2 p2;
+ Vec2 p3; // Not used for Bezier2
+ };
+ Array<Vec2> ctrl; // Bezier
+ struct { // Parametric
+ ParametricVec2 path_function;
+ ParametricVec2 path_gradient;
+ Vec2 reference;
+ void* func_data;
+ union {
+ void* grad_data;
+ double step;
+ };
+ };
+ };
+
+ void print() const;
+ Vec2 gradient(double u, const double* trafo) const;
+ Vec2 eval(double u, const double* trafo) const;
+};
+
+struct RobustPathElement {
+ Tag tag;
+
+ // These arrays should have the same count as subpath_array
+ Array<Interpolation> width_array;
+ Array<Interpolation> offset_array;
+
+ // Both end_width and end_offset should be initialized to the initial path
+ // width and offset. After that they will hold the last values used in the
+ // construction functions.
+ double end_width;
+ double end_offset;
+
+ EndType end_type;
+ Vec2 end_extensions;
+ EndFunction end_function;
+ void* end_function_data; // User data for end_function
+};
+
+struct RobustPath {
+ // Last point on the path. It should be initialized to the path origin on
+ // the creation of a new path.
+ Vec2 end_point;
+
+ Array<SubPath> subpath_array; // Path spine
+
+ RobustPathElement* elements; // Array with count num_elements
+ uint64_t num_elements;
+
+ // Numeric tolerance for intersection finding and curve approximation
+ double tolerance;
+
+ uint64_t max_evals; // Maximal number of evaluations per function
+ double width_scale; // Width scale accumulated from path transforms
+ double offset_scale; // Offset scale accumulated from path transforms
+
+ // Transformation matrix for this path. It should be initialized to the
+ // identity {1, 0, 0, 0, 1, 0}, unless you have a reason not to. It
+ // transforms a point (x, y) to (xt, yt) with:
+ // xt = x * trafo[0] + y * trafo[1] + trafo[2]
+ // yt = x * trafo[3] + y * trafo[4] + trafo[5]
+ double trafo[6];
+
+ // If simple_path is true, all elements will be treated as if they have
+ // constant widths (using the first elements in their respective
+ // half_width_and_offset arrays) and saved as paths, not polygonal
+ // boundaries.
+ bool simple_path;
+
+ // Flag indicating whether the width of the path elements should be scaled
+ // when scaling the path (manually or through references).
+ bool scale_width;
+
+ Repetition repetition;
+ Property* properties;
+ // Used by the python interface to store the associated PyObject* (if any).
+ // No functions in gdstk namespace should touch this value!
+ void* owner;
+
+ // These are initialization routines to facilitate the creation of new
+ // robustpaths. In versions with argument num_elements_, the elements
+ // array will be dynamically allocated (and num_elements properly set).
+ // Otherwise, num_elements and elements are expected to be already
+ // allocated and set. Arguments width, offset and tag can be single values
+ // (which are applied to all elements) or arrays with count num_elements,
+ // one value for each path element. Argument separation is the desired
+ // distance between adjacent elements.
+ void init(const Vec2 initial_position, double width, double offset, double tolerance_,
+ uint64_t max_evals_, Tag tag);
+ void init(const Vec2 initial_position, const double* width, const double* offset,
+ double tolerance_, uint64_t max_evals_, const Tag* tag);
+ void init(const Vec2 initial_position, uint64_t num_elements_, double width, double separation,
+ double tolerance_, uint64_t max_evals_, Tag tag);
+ void init(const Vec2 initial_position, uint64_t num_elements_, const double* width,
+ const double* offset, double tolerance_, uint64_t max_evals_, const Tag* tag);
+
+ void print(bool all) const;
+
+ void clear();
+
+ // This path instance must be zeroed before copy_from
+ void copy_from(const RobustPath& path);
+
+ void translate(const Vec2 v);
+ void scale(double scale, const Vec2 center);
+ void mirror(const Vec2 p0, const Vec2 p1);
+ void rotate(double angle, const Vec2 center);
+
+ // Transformations are applied in the order of arguments, starting with
+ // magnification and translating by origin at the end. This is equivalent
+ // to the transformation defined by a Reference with the same arguments.
+ void transform(double magnification, bool x_reflection, double rotation, const Vec2 origin);
+
+ // Append the copies of this path defined by its repetition to result.
+ void apply_repetition(Array<RobustPath*>& result);
+
+ // These functions are equivalent to those for curves (curve.h), with the
+ // addition of width and offset, which can be NULL (no width or offset
+ // changes) or arrays with count num_elements.
+ void horizontal(double coord_x, const Interpolation* width, const Interpolation* offset,
+ bool relative);
+ void vertical(double coord_y, const Interpolation* width, const Interpolation* offset,
+ bool relative);
+ void segment(const Vec2 end_point, const Interpolation* width, const Interpolation* offset,
+ bool relative);
+ void cubic(const Vec2 point1, const Vec2 point2, const Vec2 point3, const Interpolation* width,
+ const Interpolation* offset, bool relative);
+ void cubic_smooth(const Vec2 point2, const Vec2 point3, const Interpolation* width,
+ const Interpolation* offset, bool relative);
+ void quadratic(const Vec2 point1, const Vec2 point2, const Interpolation* width,
+ const Interpolation* offset, bool relative);
+ void quadratic_smooth(const Vec2 point2, const Interpolation* width,
+ const Interpolation* offset, bool relative);
+ void bezier(const Array<Vec2> point_array, const Interpolation* width,
+ const Interpolation* offset, bool relative);
+ void interpolation(const Array<Vec2> point_array, double* angles, bool* angle_constraints,
+ Vec2* tension, double initial_curl, double final_curl, bool cycle,
+ const Interpolation* width, const Interpolation* offset, bool relative);
+ void arc(double radius_x, double radius_y, double initial_angle, double final_angle,
+ double rotation, const Interpolation* width, const Interpolation* offset);
+ void turn(double radius, double angle, const Interpolation* width, const Interpolation* offset);
+ void parametric(ParametricVec2 curve_function, void* func_data, ParametricVec2 curve_gradient,
+ void* grad_data, const Interpolation* width, const Interpolation* offset,
+ bool relative);
+ uint64_t commands(const CurveInstruction* items, uint64_t count);
+
+ // These functions retrieve the position and gradient of the path spine at
+ // parametric value u, in which 0 <= u <= path.subpath_array.count. For
+ // integer values of u, uses the end position of the previous subpath or
+ // the initial position of the next depending on whether from_below is true
+ // or false, respectively. Note that this functions does not calculate
+ // intersections, it evaluates a single subpath.
+ Vec2 position(double u, bool from_below) const;
+ Vec2 gradient(double u, bool from_below) const;
+
+ // These functions are similar to the ones above, except they retrieve the
+ // width and offset of all path elements at the desired parametric value.
+ // The results are written to the result array, which must have count at
+ // least path.num_elements.
+ void width(double u, bool from_below, double* result) const;
+ void offset(double u, bool from_below, double* result) const;
+
+ // Calculate the polygonal spine of this path and append the resulting
+ // curve to result.
+ ErrorCode spine(Array<Vec2>& result) const;
+
+ // Calculate the center of an element of this path and append the resulting
+ // curve to result.
+ ErrorCode element_center(const RobustPathElement* el, Array<Vec2>& result) const;
+
+ // Append the polygonal representation of this path to result. If filter
+ // is true, only elements with the indicated tag are processed.
+ // Overlapping points are removed from the path before any processing is
+ // executed.
+ ErrorCode to_polygons(bool filter, Tag tag, Array<Polygon*>& result) const;
+
+ // These functions output the polygon in the GDSII, OASIS and SVG formats.
+ // They are not supposed to be called by the user. Because fracturing
+ // occurs at cell_to_gds, the polygons must be checked there and, if
+ // needed, fractured. Therefore, to_gds should be used only when
+ // simple_path == true to produce true GDSII path elements. The same is
+ // valid for to_oas, even though no fracturing ever occurs for OASIS files.
+ ErrorCode to_gds(FILE* out, double scaling) const;
+ ErrorCode to_oas(OasisStream& out, OasisState& state) const;
+ ErrorCode to_svg(FILE* out, double scaling, uint32_t precision) const;
+
+ private:
+ void simple_scale(double scale);
+ void simple_rotate(double angle);
+ void x_reflection();
+ void fill_widths_and_offsets(const Interpolation* width, const Interpolation* offset);
+ ErrorCode spine_intersection(const SubPath& sub0, const SubPath& sub1, double& u0,
+ double& u1) const;
+ ErrorCode center_intersection(const SubPath& sub0, const Interpolation& offset0,
+ const SubPath& sub1, const Interpolation& offset1, double& u0,
+ double& u1) const;
+ ErrorCode left_intersection(const SubPath& sub0, const Interpolation& offset0,
+ const Interpolation& width0, const SubPath& sub1,
+ const Interpolation& offset1, const Interpolation& width1,
+ double& u0, double& u1) const;
+ ErrorCode right_intersection(const SubPath& sub0, const Interpolation& offset0,
+ const Interpolation& width0, const SubPath& sub1,
+ const Interpolation& offset1, const Interpolation& width1,
+ double& u0, double& u1) const;
+ Vec2 spine_position(const SubPath& subpath, double u) const;
+ Vec2 spine_gradient(const SubPath& subpath, double u) const;
+ Vec2 center_position(const SubPath& subpath, const Interpolation& offset, double u) const;
+ Vec2 center_gradient(const SubPath& subpath, const Interpolation& offset, double u) const;
+ Vec2 left_position(const SubPath& subpath, const Interpolation& offset,
+ const Interpolation& width, double u) const;
+ Vec2 left_gradient(const SubPath& subpath, const Interpolation& offset,
+ const Interpolation& width, double u) const;
+ Vec2 right_position(const SubPath& subpath, const Interpolation& offset,
+ const Interpolation& width, double u) const;
+ Vec2 right_gradient(const SubPath& subpath, const Interpolation& offset,
+ const Interpolation& width, double u) const;
+ void spine_points(const SubPath& subpath, double u0, double u1, Array<Vec2>& result) const;
+ void center_points(const SubPath& subpath, const Interpolation& offset, double u0, double u1,
+ Array<Vec2>& result) const;
+ void left_points(const SubPath& subpath, const Interpolation& offset,
+ const Interpolation& width, double u0, double u1, Array<Vec2>& result) const;
+ void right_points(const SubPath& subpath, const Interpolation& offset,
+ const Interpolation& width, double u0, double u1, Array<Vec2>& result) const;
+};
+
// Style used in SVG output. Value is the SVG style to be applied to elements
+// with the given tag, e.g., "stroke: #D04030; fill: #D89080;"
+struct Style {
+ Tag tag;
+ char* value;
+};
+
+// Hash map of styles indexed by tag
+struct StyleMap {
+ uint64_t capacity; // allocated capacity
+ uint64_t count; // number of items in the map
+ Style* items; // array with length capacity
+
+ void print(bool all) const;
+
+ // Deallocates the whole map, including its contents
+ void clear();
+
+ // The instance should be zeroed before using copy_from
+ void copy_from(const StyleMap& map);
+
+ // Internal use
+ void resize(uint64_t new_capacity);
+ Style* get_slot(Tag tag) const;
+
+ // Value is internally allocated and copied
+ void set(Tag tag, const char* value);
+ const char* get(Tag tag) const;
+ bool del(Tag tag);
+
+ // Function to iterate over all values in the map:
+ // for (Style* style = style_map.next(NULL); style;
+ // style = style_map.next(style)) {…}
+ Style* next(const Style* current) const;
+};
+
// Error codes
+enum struct ErrorCode {
+ NoError = 0,
+ // Warnings
+ BooleanError,
+ IntersectionNotFound,
+ MissingReference,
+ UnsupportedRecord,
+ UnofficialSpecification,
+ InvalidRepetition,
+ Overflow,
+ // Errors
+ ChecksumError,
+ OutputFileOpenError,
+ InputFileOpenError,
+ InputFileError,
+ FileError,
+ InvalidFile,
+ InsufficientMemory,
+ ZlibError,
+};
+
+// Tag encapsulates layer and data (text) type. The implementation details
+// might change in the future. The only guarantee is that a zeroed Tag
+// indicates layer 0 and type 0.
+typedef uint64_t Tag;
+inline Tag make_tag(uint32_t layer, uint32_t type) {
+ return ((uint64_t)type << 32) | (uint64_t)layer;
+};
+inline uint32_t get_layer(Tag tag) { return (uint32_t)tag; };
+inline uint32_t get_type(Tag tag) { return (uint32_t)(tag >> 32); };
+inline void set_layer(Tag& tag, uint32_t layer) { tag = make_tag(layer, get_type(tag)); };
+inline void set_type(Tag& tag, uint32_t type) { tag = make_tag(get_layer(tag), type); };
+
+// Argument between 0 and 1, plus user data
+typedef double (*ParametricDouble)(double, void*);
+
+// Argument between 0 and 1, plus user data
+typedef Vec2 (*ParametricVec2)(double, void*);
+
+// Arguments: first_point, first_direction, second_point, second_direction,
+// user data
+typedef Array<Vec2> (*EndFunction)(const Vec2, const Vec2, const Vec2, const Vec2, void*);
+
+// Arguments: first_point, first_direction, second_point, second_direction,
+// center, width, user data
+typedef Array<Vec2> (*JoinFunction)(const Vec2, const Vec2, const Vec2, const Vec2, const Vec2,
+ double, void*);
+
+// Arguments: radius, initial_angle, final_angle, center, user data
+typedef Array<Vec2> (*BendFunction)(double, double, double, const Vec2, void*);
+
+// Returns new dynamically allocated memory. If len if not NULL, it is set to
+// the length of the string (including the null termination).
+char* copy_string(const char* str, uint64_t* len);
+
+// If true, m is set to the multiplicative factor, i.e., angle = 0.5 * M_PI * m
+bool is_multiple_of_pi_over_2(double angle, int64_t& m);
+
+// Number of points needed to approximate an arc within some tolerance
+uint64_t arc_num_points(double angle, double radius, double tolerance);
+
+double elliptical_angle_transform(double angle, double radius_x, double radius_y);
+
+// Distance squared from p to the line defined by p1 and p2
+double distance_to_line_sq(const Vec2 p, const Vec2 p1, const Vec2 p2);
+
+// Distance from p to the line defined by p1 and p2
+double distance_to_line(const Vec2 p, const Vec2 p1, const Vec2 p2);
+
+// Finds the intersection between lines defined by point p0 and direction ut0
+// (unit vector along the line) and by point p1 and direction ut1. Scalars u0
+// and u1 can be used to determine the intersection point:
+// p = p0 + u0 * ut0 == p1 + u1 * ut1
+void segments_intersection(const Vec2 p0, const Vec2 ut0, const Vec2 p1, const Vec2 ut1, double& u0,
+ double& u1);
+
+void scale_and_round_array(const Array<Vec2> points, double scaling, Array<IntVec2>& scaled_points);
+
+// Swap to big-endian (do nothing if the host is big-endian)
+void big_endian_swap16(uint16_t* buffer, uint64_t n);
+void big_endian_swap32(uint32_t* buffer, uint64_t n);
+void big_endian_swap64(uint64_t* buffer, uint64_t n);
+
+// Swap to little-endian (do nothing if the host is little-endian)
+void little_endian_swap16(uint16_t* buffer, uint64_t n);
+void little_endian_swap32(uint32_t* buffer, uint64_t n);
+void little_endian_swap64(uint64_t* buffer, uint64_t n);
+
+// Update the checksum32 of checksum with count values from bytes
+uint32_t checksum32(uint32_t checksum, const uint8_t* bytes, uint64_t count);
+
+Vec2 eval_line(double t, const Vec2 p0, const Vec2 p1);
+
+// Quadratic Bézier defined by control points p0, p1 and p2 at 0 ≤ t ≤ 1
+Vec2 eval_bezier2(double t, const Vec2 p0, const Vec2 p1, const Vec2 p2);
+
+// Cubic Bézier defined by control points p0 through p3 at 0 ≤ t ≤ 1
+Vec2 eval_bezier3(double t, const Vec2 p0, const Vec2 p1, const Vec2 p2, const Vec2 p3);
+
+// Evaluate a Bézier curve defined by count control points at 0 ≤ t ≤ 1
+Vec2 eval_bezier(double t, const Vec2* ctrl, uint64_t count);
+
+// Calculates the control points for a smooth cubic Bézier interpolation
+// following:
+//
+// John D. Hobby. “Smooth, easy to compute interpolating splines.” Discrete
+// Comput. Geom., 1:123–140, 1986.
+//
+// Calculated control points ca and cb are stored in points, which must have
+// the appropriate count and layout:
+//
+// points[3 * count] = {p[0], ca[0], cb[0],
+// p[1], ca[1], cb[1],
+// …,
+// p[count - 1], ca[count - 1], cb[count - 1]};
+//
+// The last controls are only present if cycle == true. Parameter angles can
+// be used to constrain the angle at any interpolation point by setting the
+// respective angle_constraints to true. Defaults for tension (at each
+// interpolation point) and curl should be 1.
+void hobby_interpolation(uint64_t count, Vec2* points, double* angles, bool* angle_constraints,
+ Vec2* tension, double initial_curl, double final_curl, bool cycle);
+
+// Stores the convex hull of points into result
+void convex_hull(const Array<Vec2> points, Array<Vec2>& result);
+
+// Return a global buffer with the representation of the value in fixed format
+// with a maximal precision set. This function is meant for internal use only.
+char* double_print(double value, uint32_t precision, char* buffer, size_t buffer_size);
+
+// Returns the default SVG style for a given tag. The return value points to a
+// statically allocated buffer that is overwritten in future calls to this
+// function.
+const char* default_svg_shape_style(Tag tag);
+const char* default_svg_label_style(Tag tag);
+
+// Thread-safe version of localtime.
+tm* get_now(tm& result);
+
+// FNV-1a hash function (64 bits)
+#define HASH_FNV_PRIME 0x00000100000001b3
+#define HASH_FNV_OFFSET 0xcbf29ce484222325
+template <class T>
+inline uint64_t hash(T key) {
+ uint64_t result = HASH_FNV_OFFSET;
+ uint8_t* byte = (uint8_t*)(&key);
+ for (unsigned i = sizeof(T); i > 0; i--) {
+ result ^= *byte++;
+ result *= HASH_FNV_PRIME;
+ }
+ return result;
+}
+
+inline uint64_t hash(const char* key) {
+ uint64_t result = HASH_FNV_OFFSET;
+ for (const char* c = key; *c; c++) {
+ result ^= (uint64_t)(*c);
+ result *= HASH_FNV_PRIME;
+ }
+ return result;
+}
+
+extern FILE* error_logger;
+void set_error_logger(FILE* log);
+
// Basic 2d coordinate used to describe polygon vertices and points in general
+// throughout the library
+struct Vec2 {
+ union {
+ struct {
+ double x, y;
+ };
+ struct {
+ double u, v;
+ };
+ struct {
+ double re, im;
+ };
+ double e[2];
+ };
+
+ bool operator==(const Vec2& vec) const { return e[0] == vec.e[0] && e[1] == vec.e[1]; }
+
+ bool operator!=(const Vec2& vec) const { return e[0] != vec.e[0] || e[1] != vec.e[1]; }
+
+ bool operator<(const Vec2& vec) const {
+ return e[0] < vec.e[0] || (e[0] == vec.e[0] && (e[1] < vec.e[1]));
+ }
+
+ bool operator>(const Vec2& vec) const {
+ return e[0] > vec.e[0] || (e[0] == vec.e[0] && (e[1] > vec.e[1]));
+ }
+
+ bool operator<=(const Vec2& vec) const {
+ return e[0] <= vec.e[0] || (e[0] == vec.e[0] && (e[1] <= vec.e[1]));
+ }
+
+ bool operator>=(const Vec2& vec) const {
+ return e[0] >= vec.e[0] || (e[0] == vec.e[0] && (e[1] >= vec.e[1]));
+ }
+
+ Vec2& operator+=(const Vec2& vec) {
+ e[0] += vec.e[0];
+ e[1] += vec.e[1];
+ return *this;
+ }
+
+ Vec2& operator+=(const double s) {
+ e[0] += s;
+ e[1] += s;
+ return *this;
+ }
+
+ Vec2& operator-=(const Vec2& vec) {
+ e[0] -= vec.e[0];
+ e[1] -= vec.e[1];
+ return *this;
+ }
+
+ Vec2& operator-=(const double s) {
+ e[0] -= s;
+ e[1] -= s;
+ return *this;
+ }
+
+ Vec2& operator*=(const Vec2& vec) {
+ e[0] *= vec.e[0];
+ e[1] *= vec.e[1];
+ return *this;
+ }
+
+ Vec2& operator*=(const double s) {
+ e[0] *= s;
+ e[1] *= s;
+ return *this;
+ }
+
+ Vec2& operator/=(const Vec2& vec) {
+ e[0] /= vec.e[0];
+ e[1] /= vec.e[1];
+ return *this;
+ }
+
+ Vec2& operator/=(const double s) {
+ e[0] /= s;
+ e[1] /= s;
+ return *this;
+ }
+
+ double inner(const Vec2& vec) const { return e[0] * vec.e[0] + e[1] * vec.e[1]; }
+
+ double length_sq() const { return inner(*this); }
+
+ double length() const { return (double)sqrt(length_sq()); }
+
+ double normalize() {
+ double len = length();
+ if (len > 0) {
+ e[0] /= len;
+ e[1] /= len;
+ }
+ return len;
+ }
+
+ double cross(const Vec2& vec) const { return x * vec.y - y * vec.x; }
+
+ double angle() const { return atan2(y, x); }
+
+ double angle(const Vec2& vec) const { return atan2(cross(vec), inner(vec)); }
+
+ Vec2 ortho() const { return Vec2{-e[1], e[0]}; }
+};
+
+inline Vec2 operator-(const Vec2& vec) { return Vec2{-vec.e[0], -vec.e[1]}; }
+
+inline Vec2 operator+(const Vec2& v1, const Vec2& v2) {
+ return Vec2{v1.e[0] + v2.e[0], v1.e[1] + v2.e[1]};
+}
+
+inline Vec2 operator+(const Vec2& vec, const double s) { return Vec2{vec.e[0] + s, vec.e[1] + s}; }
+
+inline Vec2 operator+(const double s, const Vec2& vec) { return Vec2{s + vec.e[0], s + vec.e[1]}; }
+
+inline Vec2 operator-(const Vec2& v1, const Vec2& v2) {
+ return Vec2{v1.e[0] - v2.e[0], v1.e[1] - v2.e[1]};
+}
+
+inline Vec2 operator-(const Vec2& vec, const double s) { return Vec2{vec.e[0] - s, vec.e[1] - s}; }
+
+inline Vec2 operator-(const double s, const Vec2& vec) { return Vec2{s - vec.e[0], s - vec.e[1]}; }
+
+inline Vec2 operator*(const Vec2& v1, const Vec2& v2) {
+ return Vec2{v1.e[0] * v2.e[0], v1.e[1] * v2.e[1]};
+}
+
+inline Vec2 operator*(const Vec2& vec, const double s) { return Vec2{vec.e[0] * s, vec.e[1] * s}; }
+
+inline Vec2 operator*(const double s, const Vec2& vec) { return Vec2{s * vec.e[0], s * vec.e[1]}; }
+
+inline Vec2 operator/(const Vec2& v1, const Vec2& v2) {
+ return Vec2{v1.e[0] / v2.e[0], v1.e[1] / v2.e[1]};
+}
+
+inline Vec2 operator/(const Vec2& vec, const double s) { return Vec2{vec.e[0] / s, vec.e[1] / s}; }
+
+inline Vec2 operator/(const double s, const Vec2& vec) { return Vec2{s / vec.e[0], s / vec.e[1]}; }
+
+inline Vec2 cplx_conj(const Vec2& z) { return Vec2{z.re, -z.im}; }
+
+inline Vec2 cplx_mul(const Vec2& z1, const Vec2& z2) {
+ return Vec2{z1.re * z2.re - z1.im * z2.im, z1.re * z2.im + z1.im * z2.re};
+}
+
+inline Vec2 cplx_inv(const Vec2& z) { return cplx_conj(z) / z.length_sq(); }
+
+inline Vec2 cplx_div(const Vec2& z1, const Vec2& z2) {
+ return cplx_mul(z1, cplx_conj(z2)) / z2.length_sq();
+}
+
+inline Vec2 cplx_from_angle(double angle) { return Vec2{cos(angle), sin(angle)}; }
+
+// Integer version of Vec2 used internally when reading of writing OASIS files
+struct IntVec2 {
+ union {
+ struct {
+ int64_t x, y;
+ };
+ struct {
+ int64_t u, v;
+ };
+ struct {
+ int64_t re, im;
+ };
+ int64_t e[2];
+ };
+
+ bool operator==(const IntVec2& vec) const { return e[0] == vec.e[0] && e[1] == vec.e[1]; }
+
+ bool operator!=(const IntVec2& vec) const { return e[0] != vec.e[0] || e[1] != vec.e[1]; }
+
+ bool operator<(const IntVec2& vec) const {
+ return e[0] < vec.e[0] || (e[0] == vec.e[0] && (e[1] < vec.e[1]));
+ }
+
+ bool operator>(const IntVec2& vec) const {
+ return e[0] > vec.e[0] || (e[0] == vec.e[0] && (e[1] > vec.e[1]));
+ }
+
+ bool operator<=(const IntVec2& vec) const {
+ return e[0] <= vec.e[0] || (e[0] == vec.e[0] && (e[1] <= vec.e[1]));
+ }
+
+ bool operator>=(const IntVec2& vec) const {
+ return e[0] >= vec.e[0] || (e[0] == vec.e[0] && (e[1] >= vec.e[1]));
+ }
+
+ IntVec2& operator+=(const IntVec2& vec) {
+ e[0] += vec.e[0];
+ e[1] += vec.e[1];
+ return *this;
+ }
+
+ IntVec2& operator+=(const int64_t s) {
+ e[0] += s;
+ e[1] += s;
+ return *this;
+ }
+
+ IntVec2& operator-=(const IntVec2& vec) {
+ e[0] -= vec.e[0];
+ e[1] -= vec.e[1];
+ return *this;
+ }
+
+ IntVec2& operator-=(const int64_t s) {
+ e[0] -= s;
+ e[1] -= s;
+ return *this;
+ }
+};
+
+inline IntVec2 operator-(const IntVec2& vec) { return IntVec2{-vec.e[0], -vec.e[1]}; }
+
+inline IntVec2 operator+(const IntVec2& v1, const IntVec2& v2) {
+ return IntVec2{v1.e[0] + v2.e[0], v1.e[1] + v2.e[1]};
+}
+
+inline IntVec2 operator+(const IntVec2& vec, const int64_t s) {
+ return IntVec2{vec.e[0] + s, vec.e[1] + s};
+}
+
+inline IntVec2 operator+(const int64_t s, const IntVec2& vec) {
+ return IntVec2{s + vec.e[0], s + vec.e[1]};
+}
+
+inline IntVec2 operator-(const IntVec2& v1, const IntVec2& v2) {
+ return IntVec2{v1.e[0] - v2.e[0], v1.e[1] - v2.e[1]};
+}
+
+inline IntVec2 operator-(const IntVec2& vec, const int64_t s) {
+ return IntVec2{vec.e[0] - s, vec.e[1] - s};
+}
+
+inline IntVec2 operator-(const int64_t s, const IntVec2& vec) {
+ return IntVec2{s - vec.e[0], s - vec.e[1]};
+}
+
These are a few examples of use of the Gdstk library that go beyond basic +geometry building. They should serve as reference for more complex tasks.
+A parametric cell is a concept present in a few layout editors to facilitate +the creation of geometries based on user-defined parameters. Gdstk does not +have a parameterized cell class, but since we are building the layout from a +programming language, the full flexibility of the language can be used.
+In this example we define a function that returns a grating coupler based on +user-defined parameters.
+def grating(
+ period, fill_frac=0.5, length=20, width=25, layer=0, datatype=0, cell_name="Grating"
+):
+ """
+ Straight grating:
+
+ Args:
+ period: Grating period.
+ fill_frac: Filling fraction of the teeth (wrt period).
+ length: Length of the grating.
+ width: Width of the grating.
+ layer: GDSII layer number
+ datatype: GDSII data type number
+
+ Return:
+ gdstk.Cell
+ """
+ result = gdstk.Cell(cell_name)
+ x = width / 2
+ w = period * fill_frac
+ result.add(
+ gdstk.rectangle(
+ (-x, y * period), (x, y * period + w), layer=layer, datatype=datatype
+ )
+ for y in range(int(length / period))
+ )
+ return result
+
This function can be used in the following manner:
+if __name__ == "__main__":
+ lib = gdstk.Library()
+
+ length = 20
+ grat1 = grating(3.5, length=length, layer=1, cell_name="Grating 1")
+ grat2 = grating(3.0, length=length, layer=1, cell_name="Grating 2")
+ lib.add(grat1, grat2)
+
+ main = lib.new_cell("Main")
+ main.add(gdstk.rectangle((0, -10), (150, 10)))
+ main.add(gdstk.Reference(grat1, (length, 0), rotation=numpy.pi / 2))
+ main.add(gdstk.Reference(grat2, (150 - length, 0), rotation=-numpy.pi / 2))
+
+ path = pathlib.Path(__file__).parent.absolute()
+ lib.write_gds(path / "pcell.gds")
+
#include <stdio.h>
+
+#include <gdstk/gdstk.hpp>
+
+using namespace gdstk;
+
+Cell* grating(double period, double fill_frac, double length, double width, Tag tag,
+ const char* name) {
+ double x = width / 2;
+ double w = period * fill_frac;
+ int64_t num = (int64_t)(length / period);
+
+ Cell* result = (Cell*)allocate_clear(sizeof(Cell));
+ result->name = copy_string(name, NULL);
+ result->polygon_array.ensure_slots(num);
+ for (int64_t i = 0; i < num; i++) {
+ double y = i * period;
+ Polygon* rect = (Polygon*)allocate(sizeof(Polygon));
+ *rect = rectangle(Vec2{-x, y}, Vec2{x, y + w}, tag);
+ result->polygon_array.append(rect);
+ }
+
+ return result;
+}
+
+int main(int argc, char* argv[]) {
+ Library lib = {};
+ lib.init("library", 1e-6, 1e-9);
+
+ double length = 20;
+
+ Cell* grat1 = grating(3.5, 0.5, length, 25, make_tag(1, 0), "Grating 1");
+ lib.cell_array.append(grat1);
+
+ Cell* grat2 = grating(3.0, 0.5, length, 25, make_tag(1, 0), "Grating 2");
+ lib.cell_array.append(grat2);
+
+ Cell* main_cell = (Cell*)allocate_clear(sizeof(Cell));
+ main_cell->name = copy_string("Main", NULL);
+ lib.cell_array.append(main_cell);
+
+ Polygon* rect = (Polygon*)allocate(sizeof(Polygon));
+ *rect = rectangle(Vec2{0, -10}, Vec2{150, 10}, 0);
+ main_cell->polygon_array.append(rect);
+
+ Reference* ref1 = (Reference*)allocate_clear(sizeof(Reference));
+ ref1->init(grat1);
+ ref1->origin = Vec2{length, 0};
+ ref1->rotation = M_PI / 2;
+ main_cell->reference_array.append(ref1);
+
+ Reference* ref2 = (Reference*)allocate_clear(sizeof(Reference));
+ ref2->type = ReferenceType::Cell, ref2->cell = grat2, ref2->origin = Vec2{150 - length, 0},
+ ref2->rotation = -M_PI / 2, ref2->magnification = 1, main_cell->reference_array.append(ref2);
+
+ lib.write_gds("pcell.gds", 0, NULL);
+
+ lib.free_all();
+ return 0;
+}
+
A GDSII parts library can be used when there are several devices that are often +used in different layouts. It can be a personal library of devices, or part of +a process design kit (PDK) offered by the company responsible for fabrication.
+Here we create a simple personal library with 3 components: an alignment mark, +a directional coupler and a Mach-Zehnder interferometer. All parts are added +to a GDSII file and saved for later. Note that the interferometer already uses +the directional coupler as a subcomponent.
+import pathlib
+import numpy
+import gdstk
+
+
+def alignment_mark(lib):
+ cross = gdstk.cross((0, 0), 50, 3, layer=1)
+ lib.new_cell("Alignment Mark").add(cross)
+
+
+def directional_coupler(lib):
+ path = gdstk.RobustPath((0, 0), [0.5, 0.5], 2, simple_path=True, layer=1)
+ path.segment((0.1, 0), relative=True)
+ path.segment((2.2, 0), offset=(0.6, "smooth"), relative=True)
+ path.segment((0.4, 0), relative=True)
+ path.segment((2.2, 0), offset=(2, "smooth"), relative=True)
+ path.segment((0.1, 0), relative=True)
+ lib.new_cell("Directinal Coupler").add(path)
+
+
+def mach_zehnder_interferometer(lib):
+ cell = lib.new_cell("MZI")
+ cell.add(gdstk.Reference("Directinal Coupler", (0, 0)))
+ cell.add(gdstk.Reference("Directinal Coupler", (75, 0)))
+
+ points = numpy.array([(5, 1), (25, 1), (25, 40), (55, 40), (55, 1), (75, 1)])
+ arm1 = gdstk.FlexPath(points, 0.5, bend_radius=15, simple_path=True, layer=1)
+ points[:, 1] *= -1
+ arm2 = gdstk.FlexPath(points, 0.5, bend_radius=15, simple_path=True, layer=1)
+ points = numpy.array([(25, 20), (25, 40), (55, 40), (55, 20)])
+ heater1 = gdstk.FlexPath(points, 2, bend_radius=15, simple_path=True, layer=10)
+ points[:, 1] *= -1
+ heater2 = gdstk.FlexPath(points, 2, bend_radius=15, simple_path=True, layer=10)
+ cell.add(arm1, arm2, heater1, heater2)
+
+
+if __name__ == "__main__":
+ lib = gdstk.Library("Photonics")
+
+ alignment_mark(lib)
+ directional_coupler(lib)
+ mach_zehnder_interferometer(lib)
+
+ path = pathlib.Path(__file__).parent.absolute()
+ lib.write_gds(path / "photonics.gds")
+
#include <stdio.h>
+
+#include <gdstk/gdstk.hpp>
+
+using namespace gdstk;
+
+Cell* alignment_mark() {
+ Cell* cell = (Cell*)allocate_clear(sizeof(Cell));
+ cell->name = copy_string("Alignment Mark", NULL);
+
+ Polygon* cross_ = (Polygon*)allocate(sizeof(Polygon));
+ *cross_ = cross(Vec2{0, 0}, 50, 3, make_tag(1, 0));
+ cell->polygon_array.append(cross_);
+
+ return cell;
+}
+
+Cell* directional_coupler() {
+ Cell* cell = (Cell*)allocate_clear(sizeof(Cell));
+ cell->name = copy_string("Directional Coupler", NULL);
+
+ double widths[] = {0.5, 0.5};
+ double offsets[] = {-1, 1};
+ Tag tags[] = {make_tag(1, 0), make_tag(1, 0)};
+ RobustPath* path = (RobustPath*)allocate_clear(sizeof(RobustPath));
+ path->init(Vec2{0, 0}, 2, widths, offsets, 0.01, 1000, tags);
+
+ Interpolation offset[4];
+ offset[0].type = InterpolationType::Smooth;
+ offset[0].initial_value = -1;
+ offset[0].final_value = -0.3;
+ offset[1].type = InterpolationType::Smooth;
+ offset[1].initial_value = 1;
+ offset[1].final_value = 0.3;
+ offset[2].type = InterpolationType::Smooth;
+ offset[2].initial_value = -0.3;
+ offset[2].final_value = -1;
+ offset[3].type = InterpolationType::Smooth;
+ offset[3].initial_value = 0.3;
+ offset[3].final_value = 1;
+
+ path->segment(Vec2{0.1, 0}, NULL, NULL, true);
+ path->segment(Vec2{2.2, 0}, NULL, offset, true);
+ path->segment(Vec2{0.4, 0}, NULL, NULL, true);
+ path->segment(Vec2{2.2, 0}, NULL, offset + 2, true);
+ path->segment(Vec2{0.1, 0}, NULL, NULL, true);
+ cell->robustpath_array.append(path);
+
+ return cell;
+}
+
+Cell* mach_zenhder_interferometer(Cell* directional_coupler_cell) {
+ Cell* cell = (Cell*)allocate_clear(sizeof(Cell));
+ cell->name = copy_string("MZI", NULL);
+
+ Reference* ref = (Reference*)allocate_clear(sizeof(Reference));
+ ref->init(directional_coupler_cell);
+ cell->reference_array.append(ref);
+
+ ref = (Reference*)allocate_clear(sizeof(Reference));
+ ref->init(directional_coupler_cell);
+ ref->origin.x = 75;
+ cell->reference_array.append(ref);
+
+ const Vec2 starting_points[] = {{5, 1}, {5, -1}, {25, 20}, {25, -20}};
+ FlexPath* path[4];
+ for (int64_t i = 0; i < COUNT(path); i++) {
+ path[i] = (FlexPath*)allocate_clear(sizeof(FlexPath));
+ path[i]->simple_path = true;
+ path[i]->init(starting_points[i], 1, i < 2 ? 0.5 : 2, 0, 0.01, make_tag(i < 2 ? 1 : 10, 0));
+ path[i]->elements[0].bend_type = BendType::Circular;
+ path[i]->elements[0].bend_radius = 15;
+ }
+
+ Vec2 arm_points[] = {{25, 1}, {25, 40}, {55, 40}, {55, 1}, {75, 1}};
+ path[0]->segment({.capacity = 0, .count = COUNT(arm_points), .items = arm_points}, NULL, NULL,
+ false);
+
+ for (int64_t i = 0; i < COUNT(arm_points); i++) arm_points[i].y = -arm_points[i].y;
+ path[1]->segment({.capacity = 0, .count = COUNT(arm_points), .items = arm_points}, NULL, NULL,
+ false);
+
+ Vec2 heater_points[] = {{25, 40}, {55, 40}, {55, 20}};
+ path[2]->segment({.capacity = 0, .count = COUNT(heater_points), .items = heater_points}, NULL,
+ NULL, false);
+
+ for (int64_t i = 0; i < COUNT(heater_points); i++) heater_points[i].y = -heater_points[i].y;
+ path[3]->segment({.capacity = 0, .count = COUNT(heater_points), .items = heater_points}, NULL,
+ NULL, false);
+
+ cell->flexpath_array.extend({.capacity = 0, .count = COUNT(path), .items = path});
+
+ return cell;
+}
+
+int main(int argc, char* argv[]) {
+ Library lib = {};
+ lib.init("Photonics", 1e-6, 1e-9);
+ lib.cell_array.append(alignment_mark());
+ Cell* directional_coupler_cell = directional_coupler();
+ lib.cell_array.append(directional_coupler_cell);
+ lib.cell_array.append(mach_zenhder_interferometer(directional_coupler_cell));
+ lib.write_gds("photonics.gds", 0, NULL);
+ lib.free_all();
+ return 0;
+}
+
The library “photonics.gds” created above is used in the design of a larger
+layout. It is imported through gdstk.read_rawcells()
so that is uses as
+little memory and processing power as possible.
Important
+Using gdstk.RawCell will only work properly when both the library and the +current layout use the same unit and precision.
+Another option for creating libraries is to store them as python modules. The +grating example in Parametric Cell can be saved in a file “photonics.py” +and imported as a Python module, as long as it can be found in the Python path +(leaving it in the current working directory is sufficient).
+import pathlib
+import numpy
+import gdstk
+import pcell
+
+
+if __name__ == "__main__":
+ path = pathlib.Path(__file__).parent.absolute()
+
+ # Check library units. In this case it is using the default units.
+ units = gdstk.gds_units(path / "photonics.gds")
+ print(f"Using unit = {units[0]}, precision = {units[1]}")
+
+ # Load the library as a dictionary of RawCell
+ pdk = gdstk.read_rawcells(path / "photonics.gds")
+
+ # Cell holding a single device (MZI)
+ dev_cell = gdstk.Cell("Device")
+ dev_cell.add(gdstk.Reference(pdk["MZI"], (-40, 0)))
+
+ # Create a grating coupler using the function imported from the
+ # pcell module (pcell.py) created earlier.
+ grating = pcell.grating(0.62, layer=2)
+ # Add 4 grating couplers to the device cell: one for each port.
+ dev_cell.add(
+ gdstk.Reference(
+ grating, (-200, -150), rotation=numpy.pi / 2, columns=2, spacing=(300, 0)
+ ),
+ gdstk.Reference(
+ grating, (200, 150), rotation=-numpy.pi / 2, columns=2, spacing=(300, 0)
+ ),
+ )
+
+ # Create a waveguide connecting a grating to a MZI port.
+ waveguide = gdstk.FlexPath((-220, -150), 20, bend_radius=15, layer=1)
+ # Grating background
+ waveguide.segment((20, 0), relative=True)
+ # Linear taper
+ waveguide.segment((-100, -150), 0.5)
+ # Connection to MZI
+ waveguide.segment([(-70, -150), (-70, -1), (-40, -1)])
+ # Since the device is symmetrical, we can create a cell with the
+ # waveguide geometry and reuse it for all 4 ports.
+ wg_cell = gdstk.Cell("Waveguide")
+ wg_cell.add(waveguide)
+
+ dev_cell.add(gdstk.Reference(wg_cell))
+ dev_cell.add(gdstk.Reference(wg_cell, x_reflection=True))
+ dev_cell.add(gdstk.Reference(wg_cell, rotation=numpy.pi))
+ dev_cell.add(gdstk.Reference(wg_cell, rotation=numpy.pi, x_reflection=True))
+
+ # Main cell with 2 devices and lithography alignment marks
+ main = gdstk.Cell("Main")
+ main.add(
+ gdstk.Reference(dev_cell, (250, 250)),
+ gdstk.Reference(dev_cell, (250, 750)),
+ gdstk.Reference(pdk["Alignment Mark"], columns=2, rows=3, spacing=(500, 500)),
+ )
+
+ lib = gdstk.Library()
+ lib.add(main, *main.dependencies(True))
+ lib.write_gds(path / "layout.gds")
+
#include <stdio.h>
+
+#include <gdstk/gdstk.hpp>
+
+using namespace gdstk;
+
+// We redefine the grating function from pcell.cpp here instead of including
+// the file because it contains a main function already. In practice, the
+// library source would only contain the relevant functions, but no main.
+Cell* grating(double period, double fill_frac, double length, double width, Tag tag,
+ const char* name) {
+ double x = width / 2;
+ double w = period * fill_frac;
+ int64_t num = (int64_t)(length / period);
+
+ Cell* result = (Cell*)allocate_clear(sizeof(Cell));
+ result->name = copy_string(name, NULL);
+ result->polygon_array.ensure_slots(num);
+ for (int64_t i = 0; i < num; i++) {
+ double y = i * period;
+ Polygon* rect = (Polygon*)allocate(sizeof(Polygon));
+ *rect = rectangle(Vec2{-x, y}, Vec2{x, y + w}, tag);
+ result->polygon_array.append(rect);
+ }
+
+ return result;
+}
+
+int main(int argc, char* argv[]) {
+ double unit = 0;
+ double precision = 0;
+
+ ErrorCode error_code = gds_units("photonics.gds", unit, precision);
+ if (error_code != ErrorCode::NoError) exit(EXIT_FAILURE);
+
+ printf("Using unit = %.3g, precision = %.3g\n", unit, precision);
+
+ Map<RawCell*> pdk = read_rawcells("photonics.gds", NULL);
+
+ Cell* dev_cell = (Cell*)allocate_clear(sizeof(Cell));
+ dev_cell->name = copy_string("Device", NULL);
+
+ Reference* mzi_ref = (Reference*)allocate_clear(sizeof(Reference));
+ mzi_ref->init(pdk.get("MZI"));
+ mzi_ref->origin = Vec2{-40, 0};
+ dev_cell->reference_array.append(mzi_ref);
+
+ Cell* grating_cell = grating(0.62, 0.5, 20, 25, make_tag(2, 0), "Grating");
+
+ // We set type of these references to Regular so that we can apply the
+ // rotation to the translation vectors v1 and v2 of the repetition. This
+ // way, the GDSII writer will create and AREF element instead of multiple
+ // SREFs. If x_reflection was set to true, that would also have to be
+ // applied to v2 for an AREF to be created.
+ Reference* grating_ref1 = (Reference*)allocate_clear(sizeof(Reference));
+ grating_ref1->init(grating_cell);
+ grating_ref1->origin = Vec2{-200, -150};
+ grating_ref1->rotation = M_PI / 2;
+ grating_ref1->repetition.type = RepetitionType::Regular;
+ grating_ref1->repetition.columns = 2;
+ grating_ref1->repetition.rows = 1;
+ grating_ref1->repetition.v1 = Vec2{0, 300};
+ grating_ref1->repetition.v2 = Vec2{1, 0};
+ dev_cell->reference_array.append(grating_ref1);
+
+ Reference* grating_ref2 = (Reference*)allocate_clear(sizeof(Reference));
+ grating_ref2->init(grating_cell);
+ grating_ref2->origin = Vec2{200, 150};
+ grating_ref2->rotation = -M_PI / 2;
+ grating_ref2->repetition.type = RepetitionType::Regular;
+ grating_ref2->repetition.columns = 2;
+ grating_ref2->repetition.rows = 1;
+ grating_ref2->repetition.v1 = Vec2{0, -300};
+ grating_ref2->repetition.v2 = Vec2{1, 0};
+
+ dev_cell->reference_array.append(grating_ref2);
+
+ FlexPath* waveguide = (FlexPath*)allocate_clear(sizeof(FlexPath));
+ waveguide->init(Vec2{-220, -150}, 1, 20, 0, 0.01, make_tag(1, 0));
+ waveguide->elements[0].bend_type = BendType::Circular;
+ waveguide->elements[0].bend_radius = 15;
+
+ waveguide->segment(Vec2{20, 0}, NULL, NULL, true);
+
+ const double w = 0.5;
+ waveguide->segment(Vec2{-100, -150}, &w, NULL, false);
+
+ Vec2 p[] = {{-70, -150}, {-70, -1}, {-40, -1}};
+ waveguide->segment({.capacity = 0, .count = COUNT(p), .items = p}, NULL, NULL, false);
+
+ Cell* wg_cell = (Cell*)allocate_clear(sizeof(Cell));
+ wg_cell->name = copy_string("Waveguide", NULL);
+ wg_cell->flexpath_array.append(waveguide);
+
+ for (uint64_t i = 0; i < 4; i++) {
+ Reference* wg_ref = (Reference*)allocate_clear(sizeof(Reference));
+ wg_ref->init(wg_cell);
+ if (i == 1) {
+ wg_ref->x_reflection = true;
+ } else if (i == 2) {
+ wg_ref->rotation = M_PI;
+ } else if (i == 3) {
+ wg_ref->rotation = M_PI;
+ wg_ref->x_reflection = true;
+ }
+ dev_cell->reference_array.append(wg_ref);
+ }
+
+ Cell* main_cell = (Cell*)allocate_clear(sizeof(Cell));
+ main_cell->name = copy_string("Main", NULL);
+
+ for (uint64_t i = 0; i < 2; i++) {
+ Reference* dev_ref = (Reference*)allocate_clear(sizeof(Reference));
+ dev_ref->init(dev_cell);
+ dev_ref->origin = i == 0 ? Vec2{250, 250} : Vec2{250, 750};
+ main_cell->reference_array.append(dev_ref);
+ }
+
+ Reference* align_ref = (Reference*)allocate_clear(sizeof(Reference));
+ align_ref->init(pdk.get("Alignment Mark"));
+ align_ref->repetition = {RepetitionType::Rectangular, 2, 3, Vec2{500, 500}};
+ main_cell->reference_array.append(align_ref);
+
+ Library lib = {};
+ lib.init("library", unit, precision);
+ lib.cell_array.append(main_cell);
+
+ Map<Cell*> dependencies = {};
+ main_cell->get_dependencies(true, dependencies);
+ dependencies.to_array(lib.cell_array);
+ dependencies.clear();
+
+ Map<RawCell*> raw_dependencies = {};
+ main_cell->get_raw_dependencies(true, raw_dependencies);
+ raw_dependencies.to_array(lib.rawcell_array);
+ raw_dependencies.clear();
+
+ lib.write_gds("layout.gds", 0, NULL);
+
+ lib.free_all();
+
+ // Library::free_all does not free RawCells
+ for (MapItem<RawCell*>* item = pdk.next(NULL); item; item = pdk.next(item)) {
+ item->value->clear();
+ free_allocation(item->value);
+ }
+ pdk.clear();
+
+ return 0;
+}
+
Merging two or more libraries is only a matter of adding all the cells from one +into the other. Extra cells can be latter added, of course, to create new top +level cells with references from both originals. In this example, we only +merge two GDSII files into a new one—which will end up with 2 top level +cells—and take care of renaming all cells so that they don’t collide.
+import pathlib
+import gdstk
+
+
+def make_first_lib(filename):
+ lib = gdstk.Library("First")
+ main = lib.new_cell("Main")
+ main.add(*gdstk.text("First library", 10, (0, 0)))
+ ref1 = lib.new_cell("Square")
+ ref1.add(gdstk.rectangle((-15, 0), (-5, 10)))
+ main.add(gdstk.Reference(ref1))
+ ref2 = lib.new_cell("Circle")
+ ref2.add(gdstk.ellipse((0, 0), 4))
+ ref1.add(gdstk.Reference(ref2, (-10, 5)))
+ lib.write_gds(filename)
+
+
+def make_second_lib(filename):
+ lib = gdstk.Library("Second")
+ main = lib.new_cell("Main")
+ main.add(*gdstk.text("Second library", 10, (0, 0)))
+ ref = lib.new_cell("Circle")
+ ref.add(gdstk.ellipse((-10, 5), 5))
+ main.add(gdstk.Reference(ref))
+ lib.write_gds(filename)
+
+
+if __name__ == "__main__":
+ path = pathlib.Path(__file__).parent.absolute()
+
+ # First we create the two libraries we'll be merging
+ make_first_lib(path / "lib1.gds")
+ make_second_lib(path / "lib2.gds")
+
+ # Now we load the existing libraries
+ lib1 = gdstk.read_gds(path / "lib1.gds")
+ lib2 = gdstk.read_gds(path / "lib2.gds")
+
+ # We add all cells from the second library to the first
+ lib1_cell_names = {c.name for c in lib1.cells}
+ for cell in lib2.cells:
+ # We must check that all names are unique within the merged library
+ if cell.name in lib1_cell_names:
+ cell.name += "-lib2"
+ assert cell.name not in lib1_cell_names
+ # Now we add the cell and update the set of names
+ lib1.add(cell)
+ lib1_cell_names.add(cell.name)
+
+ lib1.write_gds(path / "merging.gds")
+
#include <stdio.h>
+
+#include <gdstk/gdstk.hpp>
+
+using namespace gdstk;
+
+void make_first_lib(const char* filename) {
+ char lib_name[] = "First";
+ Library lib = {.name = lib_name, .unit = 1e-6, .precision = 1e-9};
+
+ char main_cell_name[] = "Main";
+ Cell main_cell = {.name = main_cell_name};
+ lib.cell_array.append(&main_cell);
+
+ Array<Polygon*> allocated_polygons = {};
+ text("First library", 10, Vec2{0, 0}, false, 0, allocated_polygons);
+ main_cell.polygon_array.extend(allocated_polygons);
+
+ char square_cell_name[] = "Square";
+ Cell square_cell = {.name = square_cell_name};
+ lib.cell_array.append(&square_cell);
+
+ Polygon square = rectangle(Vec2{-15, 0}, Vec2{-5, 10}, 0);
+ square_cell.polygon_array.append(&square);
+
+ Reference square_referece = {
+ .type = ReferenceType::Cell,
+ .cell = &square_cell,
+ .magnification = 1,
+ };
+ main_cell.reference_array.append(&square_referece);
+
+ char circle_cell_name[] = "Circle";
+ Cell circle_cell = {.name = circle_cell_name};
+ lib.cell_array.append(&circle_cell);
+
+ Polygon circle = ellipse(Vec2{0, 0}, 4, 4, 0, 0, 0, 0, 0.01, 0);
+ circle_cell.polygon_array.append(&circle);
+
+ Reference circle_referece = {
+ .type = ReferenceType::Cell,
+ .cell = &circle_cell,
+ .origin = {-10, 5},
+ .magnification = 1,
+ };
+ square_cell.reference_array.append(&circle_referece);
+
+ lib.write_gds(filename, 0, NULL);
+
+ lib.cell_array.clear();
+ main_cell.polygon_array.clear();
+ main_cell.reference_array.clear();
+ square_cell.polygon_array.clear();
+ square_cell.reference_array.clear();
+ circle_cell.polygon_array.clear();
+ square.clear();
+ circle.clear();
+ for (uint64_t i = 0; i < allocated_polygons.count; i++) {
+ allocated_polygons[i]->clear();
+ free_allocation(allocated_polygons[i]);
+ }
+ allocated_polygons.clear();
+}
+
+void make_second_lib(const char* filename) {
+ char lib_name[] = "Second";
+ Library lib = {.name = lib_name, .unit = 1e-6, .precision = 1e-9};
+
+ char main_cell_name[] = "Main";
+ Cell main_cell = {.name = main_cell_name};
+ lib.cell_array.append(&main_cell);
+
+ Array<Polygon*> allocated_polygons = {};
+ text("Second library", 10, Vec2{0, 0}, false, 0, allocated_polygons);
+ main_cell.polygon_array.extend(allocated_polygons);
+
+ char circle_cell_name[] = "Circle";
+ Cell circle_cell = {.name = circle_cell_name};
+ lib.cell_array.append(&circle_cell);
+
+ Polygon circle = ellipse(Vec2{-10, 5}, 5, 5, 0, 0, 0, 0, 0.01, 0);
+ circle_cell.polygon_array.append(&circle);
+
+ Reference circle_referece = {
+ .type = ReferenceType::Cell,
+ .cell = &circle_cell,
+ .magnification = 1,
+ };
+ main_cell.reference_array.append(&circle_referece);
+
+ lib.write_gds(filename, 0, NULL);
+
+ lib.cell_array.clear();
+ main_cell.polygon_array.clear();
+ main_cell.reference_array.clear();
+ circle_cell.polygon_array.clear();
+ circle.clear();
+ for (uint64_t i = 0; i < allocated_polygons.count; i++) {
+ allocated_polygons[i]->clear();
+ free_allocation(allocated_polygons[i]);
+ }
+ allocated_polygons.clear();
+}
+
+int main(int argc, char* argv[]) {
+ make_first_lib("lib1.gds");
+ make_second_lib("lib2.gds");
+
+ Library lib1 = read_gds("lib1.gds", 0, 1e-2, NULL, NULL);
+ Library lib2 = read_gds("lib2.gds", 0, 1e-2, NULL, NULL);
+
+ // We could use a hash table to make this more efficient, but we're aiming
+ // for simplicity.
+ for (uint64_t i = 0; i < lib2.cell_array.count; i++) {
+ Cell* cell = lib2.cell_array[i];
+ for (uint64_t j = 0; j < lib1.cell_array.count; j++) {
+ if (strcmp(cell->name, lib1.cell_array[j]->name) == 0) {
+ uint64_t len = strlen(cell->name);
+ cell->name = (char*)reallocate(cell->name, len + 6);
+ strcpy(cell->name + len, "-lib2");
+ // We should make sure the new name is also unique, but we are
+ // skipping that.
+ break;
+ }
+ }
+ lib1.cell_array.append(cell);
+ }
+
+ lib1.write_gds("merging.gds", 0, NULL);
+
+ // Avoid double-freeing cells from lib2
+ lib2.cell_array.clear();
+ lib2.free_all();
+ lib1.free_all();
+
+ return 0;
+}
+
Geometry transformations can be accomplished in several ways. Individual
+polygons or paths can be transformed by their respective methods
+(gdstk.Polygon.scale()
, gdstk.FlexPath.rotate()
,
+gdstk.RobustPath.translate()
, etc.).
In order to transform an entire gdstk.Cell
, we can use a
+gdstk.Reference
with the desired transformation or create a
+transformed copy with gdstk.Cell.copy()
. The former has the advantage of
+using less memory, because it does not create actual copies of the geometry or
+labels, so it is generally preferable. The latter is particularly useful when
+changes to the transformed cell contents are needed and the original should not
+be modified.
import numpy
+import gdstk
+
+
+if __name__ == "__main__":
+ n = 3 # Number of unit cells around defect
+ d = 0.2 # Unit cell size
+ r = 0.05 # Circle radius
+ s = 1.5 # Scaling factor
+
+ # Create a simple unit cell
+ unit_cell = gdstk.Cell("Unit Cell")
+ unit_cell.add(gdstk.ellipse((0, 0), r, tolerance=1e-3))
+
+ # Build a resonator from a unit cell grid with a defect inside
+ ressonator = gdstk.Cell("Resonator")
+ patches = [
+ gdstk.Reference(
+ unit_cell, (-n * d, -n * d), columns=2 * n + 1, rows=n, spacing=(d, d)
+ ),
+ gdstk.Reference(
+ unit_cell, (-n * d, d), columns=2 * n + 1, rows=n, spacing=(d, d)
+ ),
+ gdstk.Reference(unit_cell, (-n * d, 0), columns=n, rows=1, spacing=(d, d)),
+ gdstk.Reference(unit_cell, (d, 0), columns=n, rows=1, spacing=(d, d)),
+ ]
+ # Defect
+ rect = gdstk.rectangle((-r / 2, -r / 2), (r / 2, r / 2))
+ # Path for illustration
+ path = gdstk.FlexPath(
+ [(-n * d, 0), (n * d, 0)],
+ r,
+ ends=(r, r),
+ simple_path=True,
+ scale_width=False,
+ layer=1,
+ )
+ ressonator.add(rect, path, *patches)
+
+ # Main output cell with the original resonator,…
+ main = gdstk.Cell("Main")
+ main.add(gdstk.Reference(ressonator))
+ main.add(*gdstk.text("Original", d, ((n + 1) * d, -d / 2)))
+
+ # … a copy created by scaling a reference to the original resonator,…
+ main.add(gdstk.Reference(ressonator, (0, (1 + s) * (n + 1) * d), magnification=s))
+ main.add(
+ *gdstk.text("Reference\nscaling", d, (s * (n + 1) * d, (1 + s) * (n + 1) * d))
+ )
+
+ # … and another copy created by copying and scaling the Cell itself.
+ ressonator_copy = ressonator.copy("Resonator Copy", magnification=s)
+ main.add(gdstk.Reference(ressonator_copy, (0, (1 + 3 * s) * (n + 1) * d)))
+ main.add(
+ *gdstk.text(
+ "Cell copy\nscaling", d, (s * (n + 1) * d, (1 + 3 * s) * (n + 1) * d)
+ )
+ )
+
#include <stdio.h>
+
+#include <gdstk/gdstk.hpp>
+
+using namespace gdstk;
+
+int main(int argc, char* argv[]) {
+ char lib_name[] = "library";
+ Library lib = {.name = lib_name, .unit = 1e-6, .precision = 1e-9};
+
+ int64_t n = 3; // Unit cells around defect
+ double d = 0.2; // Unit cell size
+ double r = 0.05; // Circle radius
+ double s = 1.5; // Scaling factor
+
+ char unit_cell_name[] = "Unit Cell";
+ Cell unit_cell = {.name = unit_cell_name};
+ lib.cell_array.append(&unit_cell);
+
+ Polygon circle = ellipse(Vec2{0, 0}, r, r, 0, 0, 0, 0, 1e-3, 0);
+ unit_cell.polygon_array.append(&circle);
+
+ char resonator_cell_name[] = "Resonator";
+ Cell resonator_cell = {.name = resonator_cell_name};
+ lib.cell_array.append(&resonator_cell);
+
+ Reference unit_refs[] = {
+ {
+ .type = ReferenceType::Cell,
+ .cell = &unit_cell,
+ .origin = Vec2{-n * d, -n * d},
+ .magnification = 1,
+ .repetition = {RepetitionType::Rectangular, (uint16_t)(2 * n + 1), (uint16_t)n,
+ Vec2{d, d}},
+ },
+ {
+ .type = ReferenceType::Cell,
+ .cell = &unit_cell,
+ .origin = Vec2{-n * d, d},
+ .magnification = 1,
+ .repetition = {RepetitionType::Rectangular, (uint16_t)(2 * n + 1), (uint16_t)n,
+ Vec2{d, d}},
+ },
+ {
+ .type = ReferenceType::Cell,
+ .cell = &unit_cell,
+ .origin = Vec2{-n * d, 0},
+ .magnification = 1,
+ .repetition = {RepetitionType::Rectangular, (uint16_t)n, 1, Vec2{d, d}},
+ },
+ {
+ .type = ReferenceType::Cell,
+ .cell = &unit_cell,
+ .origin = Vec2{d, 0},
+ .magnification = 1,
+ .repetition = {RepetitionType::Rectangular, (uint16_t)n, 1, Vec2{d, d}},
+ },
+ };
+ Reference* unit_refs_p[] = {unit_refs, unit_refs + 1, unit_refs + 2, unit_refs + 3};
+ resonator_cell.reference_array.extend({.capacity = 0, .count = 4, .items = unit_refs_p});
+
+ Polygon rect = rectangle(Vec2{-r / 2, -r / 2}, Vec2{r / 2, r / 2}, 0);
+ resonator_cell.polygon_array.append(&rect);
+
+ FlexPath path = {
+ .simple_path = true,
+ .scale_width = false,
+ };
+ path.init(Vec2{-n * d, 0}, 1, r, 0, 0.01, make_tag(1, 0));
+ path.elements[0].end_type = EndType::Extended;
+ path.elements[0].end_extensions = Vec2{r, r};
+ path.segment(Vec2{n * d, 0}, NULL, NULL, false);
+ resonator_cell.flexpath_array.append(&path);
+
+ char main_cell_name[] = "Main";
+ Cell main_cell = {.name = main_cell_name};
+ lib.cell_array.append(&main_cell);
+
+ Cell resonator_cell_copy = {};
+ lib.cell_array.append(&resonator_cell_copy);
+ resonator_cell_copy.copy_from(resonator_cell, "Resonator Copy", true);
+ for (int64_t i = 0; i < resonator_cell_copy.polygon_array.count; i++) {
+ Polygon* p = resonator_cell_copy.polygon_array[i];
+ p->scale(Vec2{s, s}, Vec2{0, 0});
+ }
+ for (int64_t i = 0; i < resonator_cell_copy.flexpath_array.count; i++) {
+ FlexPath* fp = resonator_cell_copy.flexpath_array[i];
+ fp->scale(s, Vec2{0, 0});
+ }
+ for (int64_t i = 0; i < resonator_cell_copy.robustpath_array.count; i++) {
+ RobustPath* rp = resonator_cell_copy.robustpath_array[i];
+ rp->scale(s, Vec2{0, 0});
+ }
+ for (int64_t i = 0; i < resonator_cell_copy.reference_array.count; i++) {
+ Reference* ref = resonator_cell_copy.reference_array[i];
+ ref->transform(s, false, 0, Vec2{0, 0});
+ ref->repetition.transform(s, false, 0);
+ }
+
+ Reference resonator_refs[] = {
+ {
+ .type = ReferenceType::Cell,
+ .cell = &resonator_cell,
+ .magnification = 1,
+ },
+ {
+ .type = ReferenceType::Cell,
+ .cell = &resonator_cell,
+ .origin = Vec2{0, (1 + s) * (n + 1) * d},
+ .magnification = s,
+ },
+ {
+ .type = ReferenceType::Cell,
+ .cell = &resonator_cell_copy,
+ .origin = Vec2{0, (1 + 3 * s) * (n + 1) * d},
+ .magnification = 1,
+ },
+ };
+ Reference* resonator_refs_p[] = {resonator_refs, resonator_refs + 1, resonator_refs + 2};
+ main_cell.reference_array.extend({.capacity = 0, .count = 3, .items = resonator_refs_p});
+
+ Array<Polygon*> all_text = {};
+ text("Original", d, Vec2{(n + 1) * d, -d / 2}, false, 0, all_text);
+ text("Reference\nscaling", d, Vec2{s * (n + 1) * d, (1 + s) * (n + 1) * d}, false, 0, all_text);
+ text("Cell copy\nscaling", d, Vec2{s * (n + 1) * d, (1 + 3 * s) * (n + 1) * d}, false, 0,
+ all_text);
+ main_cell.polygon_array.extend(all_text);
+
+ lib.write_gds("transforms.gds", 0, NULL);
+
+ for (uint16_t i = 0; i < all_text.count; i++) {
+ all_text[i]->clear();
+ free_allocation(all_text[i]);
+ }
+ all_text.clear();
+ circle.clear();
+ rect.clear();
+ path.clear();
+ unit_cell.polygon_array.clear();
+ resonator_cell.reference_array.clear();
+ resonator_cell.polygon_array.clear();
+ resonator_cell.flexpath_array.clear();
+ resonator_cell_copy.free_all();
+ main_cell.reference_array.clear();
+ main_cell.polygon_array.clear();
+ lib.cell_array.clear();
+ return 0;
+}
+
Note
+The SVG output does not support the scale_width attribute of paths, that
+is why the width of the path in the referencer-scaled version of the
+geometry is wider that the original. When using gdstk.Cell.copy()
,
+the attribute is respected. This is also a problem for some GDSII viewers
+and editors.
References can be effectively used to instantiate repetitive geometry across a
+layout. Repetition
is an extension of that idea which allows the
+reuse of any element without the need for creating a gdstk.Cell
. In
+fact, the creation of a gdstk.Reference
as an array is only a shortcut
+to the creation of a single reference with a rectangular (or regular)
+repetition. The following example demonstrates the use of different forms of
+repetition to avoid creating all objects in memory (the final GDSII file will
+contain all copies).
import numpy
+import gdstk
+
+
+if __name__ == "__main__":
+ # Rectangular repetition
+ square = gdstk.regular_polygon((0, 0), 0.2, 4)
+ square.repetition = gdstk.Repetition(3, 2, spacing=(1, 1))
+
+ # Regular repetition
+ triangle = gdstk.regular_polygon((0, 2.5), 0.2, 3)
+ triangle.repetition = gdstk.Repetition(3, 5, v1=(0.4, -0.3), v2=(0.4, 0.2))
+
+ # Explicit repetition
+ circle = gdstk.ellipse((3.5, 0), 0.1)
+ circle.repetition = gdstk.Repetition(offsets=[(0.5, 1), (2, 0), (1.5, 0.5)])
+
+ # X-explicit repetition
+ vline = gdstk.FlexPath([(3, 2), (3, 3.5)], 0.1, simple_path=True)
+ vline.repetition = gdstk.Repetition(x_offsets=[0.2, 0.6, 1.4, 3.0])
+
+ # Y-explicit repetition
+ hline = gdstk.RobustPath((3, 2), 0.05, simple_path=True)
+ hline.segment((6, 2))
+ hline.repetition = gdstk.Repetition(y_offsets=[0.1, 0.3, 0.7, 1.5])
+
+ main = gdstk.Cell("Main")
+ main.add(square, triangle, circle, vline, hline)
+
#include <stdio.h>
+
+#include <gdstk/gdstk.hpp>
+
+using namespace gdstk;
+
+int main(int argc, char* argv[]) {
+ char lib_name[] = "library";
+ Library lib = {.name = lib_name, .unit = 1e-6, .precision = 1e-9};
+
+ char main_cell_name[] = "Main";
+ Cell main_cell = {.name = main_cell_name};
+ lib.cell_array.append(&main_cell);
+
+ Polygon square = regular_polygon(Vec2{0, 0}, 0.2, 4, 0, 0);
+ square.repetition = {RepetitionType::Rectangular, 3, 2, Vec2{1, 1}};
+ main_cell.polygon_array.append(&square);
+
+ Polygon triangle = regular_polygon(Vec2{0, 2.5}, 0.2, 3, 0, 0);
+ triangle.repetition.type = RepetitionType::Regular;
+ triangle.repetition.columns = 3;
+ triangle.repetition.rows = 5;
+ triangle.repetition.v1 = Vec2{0.4, -0.3};
+ triangle.repetition.v2 = Vec2{0.4, 0.2};
+ main_cell.polygon_array.append(&triangle);
+
+ Polygon circle = ellipse(Vec2{3.5, 0}, 0.1, 0.1, 0, 0, 0, 0, 0.01, 0);
+ Vec2 offsets[] = {{0.5, 1}, {2, 0}, {1.5, 0.5}};
+ circle.repetition.type = RepetitionType::Explicit;
+ circle.repetition.offsets.extend({.capacity = 0, .count = COUNT(offsets), .items = offsets});
+ main_cell.polygon_array.append(&circle);
+
+ FlexPath vline = {};
+ vline.init(Vec2{3, 2}, 1, 0.1, 0, 0.01, 0);
+ vline.simple_path = true;
+ vline.segment(Vec2{3, 3.5}, NULL, NULL, false);
+ double vcoords[] = {0.2, 0.6, 1.4, 3.0};
+ vline.repetition.type = RepetitionType::ExplicitX;
+ vline.repetition.coords.extend({.capacity = 0, .count = COUNT(vcoords), .items = vcoords});
+ main_cell.flexpath_array.append(&vline);
+
+ RobustPath hline = {};
+ hline.init(Vec2{3, 2}, 1, 0.05, 0, 0.01, 1000, 0);
+ hline.simple_path = true;
+ hline.segment(Vec2{6, 2}, NULL, NULL, false);
+ double hcoords[] = {0.1, 0.3, 0.7, 1.5};
+ hline.repetition.type = RepetitionType::ExplicitY;
+ hline.repetition.coords.extend({.capacity = 0, .count = COUNT(hcoords), .items = hcoords});
+ main_cell.robustpath_array.append(&hline);
+
+ lib.write_gds("repetitions.gds", 0, NULL);
+
+ square.clear();
+ triangle.clear();
+ circle.clear();
+ vline.clear();
+ hline.clear();
+ main_cell.polygon_array.clear();
+ main_cell.flexpath_array.clear();
+ main_cell.robustpath_array.clear();
+ lib.cell_array.clear();
+ return 0;
+}
+
When geometry operations are applied to elements with repetitions, they are not +automatically applied. If desired the repetition can be manually applied +before executing the desired operation. The following example demonstrates +this use:
+import numpy
+import gdstk
+
+
+if __name__ == "__main__":
+ # X-explicit repetition
+ vline = gdstk.FlexPath([(3, 2), (3, 3.5)], 0.1, simple_path=True)
+ vline.repetition = gdstk.Repetition(x_offsets=[0.2, 0.6, 1.4, 3.0])
+
+ # Y-explicit repetition
+ hline = gdstk.RobustPath((3, 2), 0.05, simple_path=True)
+ hline.segment((6, 2))
+ hline.repetition = gdstk.Repetition(y_offsets=[0.1, 0.3, 0.7, 1.5])
+
+ # Create all copies
+ vlines = vline.apply_repetition()
+ hlines = hline.apply_repetition()
+
+ # Include original elements for boolean operation
+ vlines.append(vline)
+ hlines.append(hline)
+
+ result = gdstk.boolean(vlines, hlines, "or")
+
+ main = gdstk.Cell("Main")
+ main.add(*result)
+
#include <stdio.h>
+
+#include <gdstk/gdstk.hpp>
+
+using namespace gdstk;
+
+int main(int argc, char* argv[]) {
+ Library lib = {};
+ lib.init("library", 1e-6, 1e-9);
+
+ Cell main_cell = {};
+ main_cell.name = copy_string("Main", NULL);
+ lib.cell_array.append(&main_cell);
+
+ FlexPath vline = {};
+ vline.init(Vec2{3, 2}, 1, 0.1, 0, 0.01, 0);
+ vline.simple_path = true;
+ vline.segment(Vec2{3, 3.5}, NULL, NULL, false);
+ double vcoords[] = {0.2, 0.6, 1.4, 3.0};
+ vline.repetition.type = RepetitionType::ExplicitX;
+ vline.repetition.coords.extend({.capacity = 0, .count = COUNT(vcoords), .items = vcoords});
+
+ Array<Polygon*> vlines = {};
+ vline.to_polygons(false, 0, vlines);
+ vline.clear();
+
+ // Because we know there is only a single resulting polygon we dont need to
+ // loop here.
+ vlines[0]->apply_repetition(vlines);
+
+ RobustPath hline = {};
+ hline.init(Vec2{3, 2}, 1, 0.05, 0, 0.01, 1000, 0);
+ hline.simple_path = true;
+ hline.segment(Vec2{6, 2}, NULL, NULL, false);
+ double hcoords[] = {0.1, 0.3, 0.7, 1.5};
+ hline.repetition.type = RepetitionType::ExplicitY;
+ hline.repetition.coords.extend({.capacity = 0, .count = COUNT(hcoords), .items = hcoords});
+
+ Array<Polygon*> hlines = {};
+ hline.to_polygons(false, 0, hlines);
+ hline.clear();
+
+ // Once again, no loop needed.
+ hlines[0]->apply_repetition(vlines);
+
+ Array<Polygon*> result = {};
+ boolean(vlines, hlines, Operation::Or, 1000, result);
+ for (uint64_t i = 0; i < vlines.count; i++) {
+ vlines[i]->clear();
+ free_allocation(vlines[i]);
+ }
+ vlines.clear();
+ for (uint64_t i = 0; i < hlines.count; i++) {
+ hlines[i]->clear();
+ free_allocation(hlines[i]);
+ }
+ hlines.clear();
+
+ main_cell.polygon_array.extend(result);
+ result.clear();
+
+ lib.write_gds("apply_repetition.gds", 0, NULL);
+
+ lib.clear();
+ main_cell.free_all();
+ return 0;
+}
+
Filtering the geometry of a loaded library requires only iterating over the +desired cells and objects, testing and removing those not wanted. In this +example we load the layout created in Using a Library and remove the +polygons in layer 2 (grating teeth) and paths in layer 10 (in the MZI).
+import pathlib
+import gdstk
+
+
+if __name__ == "__main__":
+ path = pathlib.Path(__file__).parent.absolute()
+
+ # Load existing library
+ lib = gdstk.read_gds(path / "layout.gds")
+
+ for cell in lib.cells:
+ # Remove any polygons in layer 2
+ cell.filter([(2, 0)], paths=False, labels=False)
+ # Remove any paths in layer 10
+ cell.filter([(10, 0)], polygons=False, labels=False)
+
+ lib.write_gds(path / "filtered-layout.gds")
+
#include <stdio.h>
+
+#include <gdstk/gdstk.hpp>
+
+using namespace gdstk;
+
+int main(int argc, char* argv[]) {
+ ErrorCode error_code = ErrorCode::NoError;
+ Library lib = read_gds("layout.gds", 0, 1e-2, NULL, &error_code);
+ if (error_code != ErrorCode::NoError) exit(EXIT_FAILURE);
+
+ for (int64_t i = 0; i < lib.cell_array.count; i++) {
+ Cell* cell = lib.cell_array[i];
+ for (int64_t j = 0; j < cell->polygon_array.count; j++) {
+ Polygon* poly = cell->polygon_array[j];
+ // Decrement j so that we don't skip over the next polygon.
+ if (get_layer(poly->tag) == 2) {
+ cell->polygon_array.remove(j--);
+ poly->clear();
+ free_allocation(poly);
+ }
+ }
+ // Loaded libraries have no RobustPath elements
+ for (int64_t j = 0; j < cell->flexpath_array.count; j++) {
+ FlexPath* fp = cell->flexpath_array[j];
+ // All paths in loaded libraries have only 1 element.
+ // Decrement j so that we don't skip over the next path.
+ if (get_layer(fp->elements[0].tag) == 10) {
+ cell->flexpath_array.remove(j--);
+ fp->clear();
+ free_allocation(fp);
+ }
+ }
+ }
+
+ lib.write_gds("filtered-layout.gds", 0, NULL);
+ lib.free_all();
+ return 0;
+}
+
Another common use of filtering is to remove geometry in a particular region.
+In this example we create a periodic background and remove all elements that
+overlap a particular shape using gdstk.inside()
to test.
import pathlib
+import gdstk
+
+
+if __name__ == "__main__":
+ path = pathlib.Path(__file__).parent.absolute()
+
+ unit = gdstk.Cell("Unit")
+ unit.add(gdstk.cross((0, 0), 1, 0.2))
+
+ main = gdstk.Cell("Main")
+
+ # Create repeating pattern using references
+ d = 2
+ ref1 = gdstk.Reference(unit, columns=11, rows=6, spacing=(d, d * 3**0.5))
+ ref2 = gdstk.Reference(
+ unit, (d / 2, d * 3**0.5 / 2), columns=10, rows=5, spacing=(d, d * 3**0.5)
+ )
+ main.add(ref1, ref2)
+ main.flatten()
+
+ hole = gdstk.text("PY", 8 * d, (0.5 * d, 0), layer=1)
+ for pol in main.polygons:
+ if gdstk.any_inside(pol.points, hole):
+ main.remove(pol)
+
+ main.add(*hole)
+
+ gdstk.Library().add(main).write_gds(path / "pos_filtering.gds")
+
#include <stdio.h>
+
+#include <gdstk/gdstk.hpp>
+
+using namespace gdstk;
+
+int main(int argc, char* argv[]) {
+ char lib_name[] = "library";
+ Library lib = {.name = lib_name, .unit = 1e-6, .precision = 1e-9};
+
+ char unit_cell_name[] = "Unit";
+ Cell unit_cell = {.name = unit_cell_name};
+
+ Polygon cross_ = cross(Vec2{0, 0}, 1, 0.2, 0);
+ unit_cell.polygon_array.append(&cross_);
+
+ char main_cell_name[] = "Main";
+ Cell main_cell = {.name = main_cell_name};
+ lib.cell_array.append(&main_cell);
+
+ double d = 2;
+ Reference unit_refs[] = {
+ {
+ .type = ReferenceType::Cell,
+ .cell = &unit_cell,
+ .magnification = 1,
+ .repetition = {RepetitionType::Rectangular, 11, 6, Vec2{d, d * sqrt(3)}},
+ },
+ {
+ .type = ReferenceType::Cell,
+ .cell = &unit_cell,
+ .origin = Vec2{d / 2, d * sqrt(3) / 2},
+ .magnification = 1,
+ .repetition = {RepetitionType::Rectangular, 10, 5, Vec2{d, d * sqrt(3)}},
+ },
+ };
+ main_cell.reference_array.append(unit_refs);
+ main_cell.reference_array.append(unit_refs + 1);
+
+ Array<Reference*> removed_references = {};
+ main_cell.flatten(true, removed_references);
+ removed_references.clear();
+
+ Array<Polygon*> txt = {};
+ text("PY", 8 * d, Vec2{0.5 * d, 0}, false, make_tag(1, 0), txt);
+ for (uint64_t i = 0; i < main_cell.polygon_array.count; i++) {
+ Polygon* poly = main_cell.polygon_array[i];
+ if (any_inside(poly->point_array, txt)) {
+ poly->clear();
+ free_allocation(poly);
+ main_cell.polygon_array.remove(i--);
+ }
+ }
+
+ main_cell.polygon_array.extend(txt);
+ txt.clear();
+
+ lib.write_gds("pos_filtering.gds", 0, NULL);
+
+ cross_.clear();
+ for (uint64_t i = 0; i < main_cell.polygon_array.count; i++) {
+ main_cell.polygon_array[i]->clear();
+ free_allocation(main_cell.polygon_array[i]);
+ }
+ main_cell.reference_array.clear();
+ main_cell.polygon_array.clear();
+ unit_cell.polygon_array.clear();
+ lib.cell_array.clear();
+ return 0;
+}
+
Finally, gdstk.read_gds()
provides a way to only load specific layers and
+data types from a GDSII file, and methods gdstk.Cell.get_polygons()
,
+gdstk.Cell.get_paths()
, gdstk.Cell.get_labels()
can also be used to
+gather elements only from specific layers and types.
The following example shows how to add markers along a
+gdstk.RobustPath
. It uses the original parameterization of the path
+to locate the markers, following the construction sections. Markers positioned
+at a fixed distance must be calculated for each section independently.
import numpy
+import gdstk
+
+
+if __name__ == "__main__":
+ main = gdstk.Cell("Main")
+
+ # Create a path
+ path = gdstk.RobustPath((0, 0), 0.5)
+ path.segment((8, 0))
+ path.interpolation(
+ [(2, -4), (-2, -6), (-5, -8), (-4, -12)],
+ angles=[0, None, None, None, -numpy.pi / 4],
+ relative=True,
+ )
+ path.segment((3, -3), relative=True)
+ main.add(path)
+
+ # Major and minor markers
+ major = gdstk.regular_polygon((0, 0), 0.5, 6, layer=1)
+ minor = gdstk.rectangle((-0.1, -0.5), (0.1, 0.5), layer=1)
+ for s in range(path.size):
+ # A major marker is added at the start of each path section
+ m = major.copy()
+ m.translate(path.position(s))
+ main.add(m)
+ for u in numpy.linspace(0, 1, 5)[1:-1]:
+ # Each section receives 3 equally-spaced minor markers
+ # rotated to be aligned to the path direction
+ m = minor.copy()
+ grad = path.gradient(s + u)
+ m.rotate(numpy.arctan2(grad[1], grad[0]))
+ m.translate(path.position(s + u))
+ main.add(m)
+ # Add a major marker at the end of the path
+ major.translate(path.position(path.size))
+ main.add(major)
+
#include <stdio.h>
+
+#include <gdstk/gdstk.hpp>
+
+using namespace gdstk;
+
+int main(int argc, char* argv[]) {
+ Library lib = {};
+ lib.init("library", 1e-6, 1e-9);
+
+ Cell* main_cell = (Cell*)allocate_clear(sizeof(Cell));
+ main_cell->name = copy_string("Main", NULL);
+ lib.cell_array.append(main_cell);
+
+ RobustPath* path = (RobustPath*)allocate_clear(sizeof(RobustPath));
+ path->init(Vec2{0, 0}, 1, 0.5, 0, 0.01, 1000, 0);
+ path->segment(Vec2{8, 0}, NULL, NULL, false);
+
+ Vec2 points[] = {{2, -4}, {-2, -6}, {-5, -8}, {-4, -12}};
+ double angles[] = {0, 0, 0, 0, -M_PI / 4};
+ bool angle_constraints[] = {true, false, false, false, true};
+ Vec2 tension[] = {{1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1}};
+ path->interpolation({.capacity = 0, .count = COUNT(points), .items = points}, angles,
+ angle_constraints, tension, 1, 1, false, NULL, NULL, true);
+
+ path->segment(Vec2{3, -3}, NULL, NULL, true);
+ main_cell->robustpath_array.append(path);
+
+ Polygon* major = (Polygon*)allocate_clear(sizeof(Polygon));
+ *major = regular_polygon(Vec2{0, 0}, 0.5, 6, 0, make_tag(1, 0));
+ Polygon minor = rectangle(Vec2{-0.1, -0.5}, Vec2{0.1, 0.5}, make_tag(1, 0));
+
+ // Reserve space for all markers in the main cell
+ int64_t count = path->subpath_array.count;
+ main_cell->polygon_array.ensure_slots(1 + 4 * count);
+
+ for (int64_t i = 0; i < count; i++) {
+ Polygon* poly = (Polygon*)allocate_clear(sizeof(Polygon));
+ poly->copy_from(*major);
+ poly->translate(path->position(i, true));
+ main_cell->polygon_array.append(poly);
+ for (int64_t j = 1; j < 4; j++) {
+ poly = (Polygon*)allocate_clear(sizeof(Polygon));
+ poly->copy_from(minor);
+ double u = i + j / 4.0;
+ poly->rotate(path->gradient(u, true).angle(), Vec2{0, 0});
+ poly->translate(path->position(u, true));
+ main_cell->polygon_array.append(poly);
+ }
+ }
+
+ // Last marker: we use the original major marker
+ major->translate(path->end_point);
+ main_cell->polygon_array.append(major);
+ minor.clear();
+
+ lib.write_gds("path_markers.gds", 0, NULL);
+
+ lib.free_all();
+ return 0;
+}
+
In this example, a custom end function is used to provide connection pads for +electrical traces. For simplicity, it assumes that the path width does not +change in the first and last segments, which also must be long enough to +support the pad shapes, and that the pad diameter is larger than the path +width. The point where the pad connects to the trace can, optionally, be +filleted.
+import numpy
+import gdstk
+
+
+if __name__ == "__main__":
+
+ def filleted_pad(pad_radius, fillet_radius=0, tolerance=0.01):
+ def _f(p0, v0, p1, v1):
+ p0 = numpy.array(p0)
+ v0 = numpy.array(v0)
+ p1 = numpy.array(p1)
+ v1 = numpy.array(v1)
+
+ half_trace_width = 0.5 * numpy.sqrt(numpy.sum((p0 - p1) ** 2))
+ a = half_trace_width + fillet_radius
+ c = pad_radius + fillet_radius
+ b = (c**2 - a**2) ** 0.5
+ alpha = numpy.arccos(a / c)
+ gamma = numpy.arctan2(v0[1], v0[0]) + 0.5 * numpy.pi
+
+ curve = gdstk.Curve(p0 - v0 * b, tolerance=tolerance)
+ if fillet_radius > 0:
+ curve.arc(fillet_radius, gamma, gamma - alpha)
+ curve.arc(pad_radius, gamma - numpy.pi - alpha, gamma + alpha)
+ if fillet_radius > 0:
+ curve.arc(fillet_radius, gamma - numpy.pi + alpha, gamma - numpy.pi)
+
+ return curve.points()
+
+ return _f
+
+ main = gdstk.Cell("Main")
+
+ # Create a bus with 4 traces
+ bus = gdstk.FlexPath(
+ [(0, 0), (10, 5)], [3] * 4, offset=15, joins="round", ends=filleted_pad(5, 3)
+ )
+ bus.segment((20, 10), offset=6)
+ bus.segment([(40, 20), (40, 50), (80, 50)])
+ bus.segment((100, 50), offset=12)
+ main.add(bus)
+
#include <math.h>
+#include <stdio.h>
+
+#include <gdstk/gdstk.hpp>
+
+using namespace gdstk;
+
+struct FilletedPadData {
+ double pad_radius;
+ double fillet_radius;
+ double tolerance;
+};
+
+Array<Vec2> filleted_pad(const Vec2 p0, const Vec2 v0, const Vec2 p1, const Vec2 v1, void* data) {
+ FilletedPadData* pad_data = (FilletedPadData*)data;
+ double pad_radius = pad_data->pad_radius;
+ double fillet_radius = pad_data->fillet_radius;
+
+ Vec2 dp = p0 - p1;
+ double half_trace_width = 0.5 * sqrt(dp.x * dp.x + dp.y * dp.y);
+ double a = half_trace_width + fillet_radius;
+ double c = pad_radius + fillet_radius;
+ double b = sqrt(c * c + a * a);
+ double alpha = acos(a / c);
+ double gamma = atan2(v0.y, v0.x) + 0.5 * M_PI;
+
+ Curve curve = {};
+ curve.init(p0 - b * v0, pad_data->tolerance);
+ if (fillet_radius > 0) {
+ curve.arc(fillet_radius, fillet_radius, gamma, gamma - alpha, 0);
+ }
+ curve.arc(pad_radius, pad_radius, gamma - M_PI - alpha, gamma + alpha, 0);
+ if (fillet_radius > 0) {
+ curve.arc(fillet_radius, fillet_radius, gamma - M_PI + alpha, gamma - M_PI, 0);
+ }
+
+ return curve.point_array;
+}
+
+int main(int argc, char* argv[]) {
+ Library lib = {};
+ lib.init("library", 1e-6, 1e-9);
+
+ Cell* main_cell = (Cell*)allocate_clear(sizeof(Cell));
+ main_cell->name = copy_string("Main", NULL);
+ lib.cell_array.append(main_cell);
+
+ FlexPath* bus = (FlexPath*)allocate_clear(sizeof(FlexPath));
+ bus->init(Vec2{0, 0}, 4, 3, 15, 0.01, 0);
+ FilletedPadData data = {5, 3, 0.01};
+ for (int64_t i = 0; i < 4; i++) {
+ bus->elements[i].join_type = JoinType::Round;
+ bus->elements[i].end_type = EndType::Function;
+ bus->elements[i].end_function = filleted_pad;
+ bus->elements[i].end_function_data = (void*)&data;
+ }
+ bus->segment(Vec2{10, 5}, NULL, NULL, false);
+ double offsets1[] = {-9, -3, 3, 9};
+ bus->segment(Vec2{20, 10}, NULL, offsets1, false);
+ Vec2 points[] = {{40, 20}, {40, 50}, {80, 50}};
+ const Array<Vec2> point_array = {.capacity = 0, .count = COUNT(points), .items = points};
+ bus->segment(point_array, NULL, NULL, false);
+ double offsets2[] = {-18, -6, 6, 18};
+ bus->segment(Vec2{100, 50}, NULL, offsets2, false);
+
+ main_cell->flexpath_array.append(bus);
+
+ lib.write_gds("pads.gds", 0, NULL);
+
+ lib.free_all();
+ return 0;
+}
+
This example uses matplotlib to render text using
+any typeface present in the system. The glyph paths are then transformed into
+polygon arrays that can be used to create gdstk.Polygon
objects.
import gdstk
+from matplotlib.font_manager import FontProperties
+from matplotlib.textpath import TextPath
+
+
+def render_text(text, size=None, position=(0, 0), font_prop=None, tolerance=0.1):
+ precision = 0.1 * tolerance
+ path = TextPath(position, text, size=size, prop=font_prop)
+ polys = []
+ xmax = position[0]
+ for points, code in path.iter_segments():
+ if code == path.MOVETO:
+ c = gdstk.Curve(points, tolerance=tolerance)
+ elif code == path.LINETO:
+ c.segment(points.reshape(points.size // 2, 2))
+ elif code == path.CURVE3:
+ c.quadratic(points.reshape(points.size // 2, 2))
+ elif code == path.CURVE4:
+ c.cubic(points.reshape(points.size // 2, 2))
+ elif code == path.CLOSEPOLY:
+ pts = c.points()
+ if pts.size > 0:
+ poly = gdstk.Polygon(pts)
+ if pts[:, 0].min() < xmax:
+ i = len(polys) - 1
+ while i >= 0:
+ if polys[i].contain_any(*poly.points):
+ p = polys.pop(i)
+ poly = gdstk.boolean(p, poly, "xor", precision)[0]
+ break
+ elif poly.contain_any(*polys[i].points):
+ p = polys.pop(i)
+ poly = gdstk.boolean(p, poly, "xor", precision)[0]
+ i -= 1
+ xmax = max(xmax, poly.points[:, 0].max())
+ polys.append(poly)
+ return polys
+
+
+if __name__ == "__main__":
+ cell = gdstk.Cell("fonts")
+ fp = FontProperties(family="serif", style="italic")
+ polygons = render_text("Text rendering", 10, font_prop=fp)
+ cell.add(*polygons)
+
Gdstk (GDSII Tool Kit) is a C++ library for creation and manipulation of GDSII +and OASIS files. It is also available as a Python module meant to be a +successor to Gdspy.
+Key features for the creation of complex CAD layouts are included:
+Boolean operations on polygons (AND, OR, NOT, XOR) based on clipping +algorithm
Polygon offset (inward and outward rescaling of polygons)
Efficient point-in-polygon solutions for large array sets
Typical applications of Gdstk are in the fields of electronic chip design, +planar lightwave circuit design, and mechanical engineering.
+For installation instructions and other information, please check out the +GitHub repository or the README file +included with the source.
+Help support the development of Gdstk by donating:
+ +Boost Software License - Version 1.0 - August 17th, 2003
+Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the “Software”) to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following:
+The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor.
+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE.
+Cell structure.
+A cell is a fundamental structure in the layout. It stores the
+geometry, labels and references that make up the final layout. A single
+library can contain any number of independent cells, which can,
+themselves, contain references to other cells in the form of
+gdstk.Reference
.
name (str) – Cell name. It must be ASCII encoded and unique within a +library.
+Methods
+
|
+Add polygons, paths, labels and references to this cell. |
+
|
+Calculate the area of this cell. |
+
+ | Calculate the cell bounding box. |
+
+ | Calculate the convex hull of the cell. |
+
|
+Create a copy of this cell. |
+
|
+Delete the first property of this element matching a name. |
+
|
+List of cells and raw cells that are referenced by this cell. |
+
|
+Remove elements from this cell based on their layer and data/text type. |
+
|
+Transform all references into polygons, paths and labels. |
+
|
+Return a copy of all labels in the cell. |
+
|
+Return a copy of all paths in the cell. |
+
|
+Return a copy of all polygons in the cell. |
+
|
+Return the values of the first property of this element matching a name. |
+
|
+Remap layers and data/text types for all elements in this cell. |
+
|
+Remove polygons, paths, labels and references from this cell. |
+
|
+Set a property for this element. |
+
|
+Export this cell to an SVG image file. |
+
Attributes
++ | List of cell labels. |
+
+ | Cell name. |
+
+ | List of cell paths. |
+
+ | List of cell polygons. |
+
+ | Properties of this element. |
+
+ | List of cell references. |
+
Add polygons, paths, labels and references to this cell.
+Calculate the area of this cell.
+by_spec – If True
, return a dictionary with keys
+(layer, datatype)
.
Area of the geometry in this cell, optionally indexed by layer and +data type.
+Notes
+This operation can be slow because all paths and references are +included in the computation.
+Calculate the cell bounding box.
+The lower-left and upper-right corners of the bounding box of the
+cell: ((min_x, min_y), (max_x, max_y))
.
Examples
+>>> polygons = gdstk.text("F", 10, (0, 0))
+>>> f_cell = gdstk.Cell("F_CELL")
+>>> f_cell.add(*polygons)
+>>> ang = numpy.pi / 4
+>>> array_ref = gdstk.Reference(f_cell, rotation=ang)
+>>> array_ref.repetition = gdstk.Repetition(columns=3, rows=2,
+... v1=8 * numpy.exp(1j * ang), v2=10j * numpy.exp(1j * ang))
+>>> path = gdstk.FlexPath([(-5, 0), (0, -5), (5, 0)], 1,
+... simple_path=True)
+>>> main_cell = gdstk.Cell("MAIN")
+>>> main_cell.add(array_ref, path)
+>>> bbox = main_cell.bounding_box()
+>>> print(bbox)
+((-12.816310409006173, -5.707106781186548),
+ (11.313708498984761, 27.66555281392367))
+>>> polygon_bb = gdstk.rectangle(*bbox, datatype=1)
+
Notes
+This operation can be slow because all paths and references are +included in the computation.
+Calculate the convex hull of the cell.
+The convex hull is the smallest convex polygon that contains all +elements of the cell.
+Vertices of the convex hull.
+Examples
+>>> polygons = gdstk.text("F", 10, (0, 0))
+>>> f_cell = gdstk.Cell("F_CELL")
+>>> f_cell.add(*polygons)
+>>> ang = numpy.pi / 4
+>>> array_ref = gdstk.Reference(f_cell, rotation=ang)
+>>> array_ref.repetition = gdstk.Repetition(columns=3, rows=2,
+... v1=8 * numpy.exp(1j * ang), v2=10j * numpy.exp(1j * ang))
+>>> path = gdstk.FlexPath([(-5, 0), (0, -5), (5, 0)], 1,
+... simple_path=True)
+>>> main_cell = gdstk.Cell("MAIN")
+>>> main_cell.add(array_ref, path)
+>>> hull = main_cell.convex_hull()
+>>> polygon_hull = gdstk.Polygon(hull, datatype=1)
+
Notes
+This operation can be slow because all paths and references are +included in the computation.
+Create a copy of this cell.
+A transformation can be applied to the contents of the new cell. They +follow the same order as reference transformations.
+name (str) – Name of the new cell.
translation (coordinate pair or complex) – Amount to translate the +cell contents.
rotation – Rotation angle (in radians).
magnification – Scaling factor.
x_reflection – If True
, the geometry is reflected across the
+horizontal axis before rotation.
deep_copy – If True
, new elements (polygons, paths, references,
+and labels) are also created for the copied cell. If any
+transformation is applied, this value is always True
.
Copy of this cell.
+Delete the first property of this element matching a name.
+name (str) – Property name.
+List of cells and raw cells that are referenced by this cell.
+recursive – If True
, includes all dependencies recursively.
Remove elements from this cell based on their layer and data/text type.
+spec (iterable of tuples) – Tuples of (layer, data/text type) to +remove or to keep in the cell.
remove – If True, shapes whose layer and type specification are in +spec will be removed from the cell. If False, only those +shapes will be kept, while others will be removed.
polygons – Whether to filter the cell’s polygons.
paths – Whether to filter the cell’s paths.
labels – Whether to filter the cell’s labels.
Examples
+>>> # Remove all elements in layer 3, type 10:
+>>> cell.filter([(3, 10)], True)
+>>> # Remove all elements except for those on layer 5, type 2:
+>>> cell.filter([(5, 2)], False)
+
Transform all references into polygons, paths and labels.
+apply_repetitions – Define whether repetitions should be flattened +for polygons, paths and labels (reference repetitions are always +applied).
+Examples
+>>> poly1 = gdstk.Polygon([(0, 0), (1, 0), (0.5, 1)])
+>>> cell1 = gdstk.Cell('CELL_1')
+>>> cell1.add(poly1)
+>>> poly2 = gdstk.Polygon([(1, 0), (1.5, 1), (0.5, 1)], layer=1)
+>>> ref = gdstk.Reference(cell1)
+>>> ref.repetition = gdstk.Repetition(columns=2, rows=2,
+... spacing=(1, 1))
+>>> cell2 = gdstk.Cell('CELL_2')
+>>> cell2.add(poly2, ref)
+>>> print(len(cell2.polygons), len(cell2.references),
+... len(cell2.dependencies(True)))
+1 1 1
+>>> cell2.flatten()
+>>> print(len(cell2.polygons), len(cell2.references),
+... len(cell2.dependencies(True)))
+5 0 0
+
Return a copy of all labels in the cell.
+apply_repetitions – Define whether repetitions should be applied in +the created labels.
depth – If non negative, indicates the number of reference levels
+processed recursively. A value of 0 will result in no references
+being visited. A value of None
(the default) or a negative
+integer will include all reference levels below the cell.
layer – If set, only labels in the defined layer and text type are +returned.
texttype – If set, only labels in the defined layer and text type +are returned.
Notes
+Arguments layer
and texttype
must both be set to integers
+for the filtering to be executed. If either one is None
they
+are both ignored.
Return a copy of all paths in the cell.
+apply_repetitions – Define whether repetitions should be applied in +the created paths.
depth – If non negative, indicates the number of reference levels
+processed recursively. A value of 0 will result in no references
+being visited. A value of None
(the default) or a negative
+integer will include all reference levels below the cell.
layer – If set, only paths in the defined layer and data type are +returned.
datatype – If set, only paths in the defined layer and data type are +returned.
Notes
+Arguments layer
and datatype
must both be set to integers
+for the filtering to be executed. If either one is None
they
+are both ignored.
Return a copy of all polygons in the cell.
+apply_repetitions – Define whether repetitions should be applied in +the created polygons.
include_paths – If True
, polygonal representation of paths are
+also included in the result.
depth – If non negative, indicates the number of reference levels
+processed recursively. A value of 0 will result in no references
+being visited. A value of None
(the default) or a negative
+integer will include all reference levels below the cell.
layer – If set, only polygons in the defined layer and data type are +returned.
datatype – If set, only polygons in the defined layer and data type +are returned.
Notes
+Arguments layer
and datatype
must both be set to integers
+for the filtering to be executed. If either one is None
they
+are both ignored.
Return the values of the first property of this element matching a name.
+name (str) – Property name.
+List of property values. If no property is found,
+None
is returned.
list or None
+List of cell labels.
+Notes
+This attribute is read-only.
+Cell name.
+List of cell paths.
+Notes
+This attribute is read-only.
+List of cell polygons.
+Notes
+This attribute is read-only.
+Properties of this element.
+Properties are represented as a list of lists, each containing the +property name followed by its values.
+List of cell references.
+Notes
+This attribute is read-only.
+Remap layers and data/text types for all elements in this cell.
+layer_type_map – Dictionary mapping existing (layer, type) tuples to +desired (layer, type) tuples.
+Notes
+References in this cell are not affected. To remap those, loop +over them with get_dependencies. To remap a whole libarry, use +Library.remap.
+Remove polygons, paths, labels and references from this cell.
+Examples
+>>> polygons = gdstk.text("Filter dots\nin i and j!", 8, (0, 0))
+>>> cell = gdstk.Cell("FILTERED")
+>>> cell.add(*polygons)
+>>> dots = [poly for poly in cell.polygons if poly.area() < 2]
+>>> cell.remove(*dots)
+
Set a property for this element.
+The property name does not have to be unique. Multiple properties can +have the same name.
+name (str) – Property name.
value (str, bytes, number, or sequence of those) – Values associated +with the property.
Notes
+These properties can be used to associate custom metadata with an +element, but general properties are not supported by GDSII files, +only OASIS. Use the specific methods to access GDSII properties.
+Export this cell to an SVG image file. Colors and attributes must follow +SVG specification.
+outfile (str or pathlib.Path) – Name of the output file.
scaling – Scaling factor for the whole geometry.
precision (positive integer) – Maximum number of digits for +coordinates after scaling.
shape_style (dict) – SVG attributes for each layer and data type.
label_style (dict) – SVG attributes for each layer and text type.
background (str) – Image background color.
pad (number, str) – Viewport margin around the image content. It can +be specified as an absolute dimension or a percentage of the +largest image dimension.
sort_function (callable) – If set, the polygons on each cell will be
+sorted according to this function. It must accept 2 polygons and
+return True
if the first one is below the second.
Notes
+Labels in referenced cells will be affected by the the reference +transformation, including magnification.
+Examples
+>>> # (layer, datatype) = (0, 1)
+>>> poly1 = gdstk.ellipse((0, 0), (13, 10), datatype=1)
+>>> # (layer, datatype) = (1, 0)
+>>> poly2 = gdstk.ellipse((0, 0), (10, 7), layer=1)
+>>> (layer, texttype) = (3, 2)
+>>> label = gdstk.Label("Example", (0, 0), layer=3, texttype=2)
+>>> cell = gdstk.Cell("SVG")
+>>> cell.add(poly1, poly2, label)
+>>> cell.write_svg(
+... "example.svg",
+... background="none",
+... shape_style={(0, 1): {"fill": "none",
+... "stroke": "black",
+... "stroke-dasharray": "8,8"}},
+... label_style={(3, 2): {"fill": "none",
+... "stroke": "red",
+... "font-size": "32px"}},
+... pad="5%",
+... sort_function=lambda p1, p2: p1.layer < p2.layer,
+... )
+
Multi-step GDSII stream file writer.
+When a design is too large to be completely created in memory, it is not
+possible to create a gdstk.Library
to save it as a GDSII file.
+In such cases, it might be possible to still create the desired design
+creating, saving and deleting one cell at a time. Saving one cell at a
+time can be done thru this class.
outfile (str or pathlib.Path) – Name of the output file.
unit – User units in meters.
precision – Desired precision to store the units once written to a +GDSII file.
max_points – Maximal number of vertices per polygon. Polygons with +more vertices that this are automatically fractured.
timestamp (datetime object) – Timestamp to be stored in the GDSII
+file. If None
, the current time is used.
>>> writer = gdstk.GdsWriter()
+>>> cell1 = some_function_that_creates_a_huge_cell()
+>>> writer.write(cell1)
+>>> del cell1
+>>> cell2 = some_function_that_creates_a_huge_cell()
+>>> writer.write(cell2)
+>>> del cell2
+>>> writer.close()
+
See also
+ +Methods
+
|
+Finish writing the output file and close it. |
+
|
+Write cells to the output file. |
+
Finish writing the output file and close it.
+Write cells to the output file.
+Text objects with out geometrical information.
+Labels do not create any new geometry in the layout. They can be used to
+add descriptions, flags, or comments to the design. For polygonal text,
+gdstk.text()
should be used instead.
text (str) – Text string.
origin (coordinate pair or complex) – Label position.
anchor (str) – Origin position with respect to the label text. One of +“n”, “s”, “e”, “w”, “o”, “ne”, “nw”, “se”, or “sw”.
rotation – Rotation angle (in radians).
magnification – Scaling factor.
x_reflection – If True
, the label is reflected across the
+horizontal axis before rotation.
layer – layer number assigned to this label.
texttype – text type number assigned to this label.
Examples
+>>> frame = gdstk.rectangle((-2, -1), (2, 1), datatype=1)
+>>> label_o = gdstk.Label("Center", (0, 0), rotation=numpy.pi / 6)
+>>> label_n = gdstk.Label("North", (0, 1), "n")
+>>> label_s = gdstk.Label("South", (0, -1), "s")
+>>> label_e = gdstk.Label("East", (2, 0), "e")
+>>> label_w = gdstk.Label("West", (-2, 0), "w")
+>>> label_ne = gdstk.Label("Northeast", (2, 1), "ne")
+>>> label_se = gdstk.Label("Southeast", (2, -1), "se")
+>>> label_nw = gdstk.Label("Northwest", (-2, 1), "nw")
+>>> label_sw = gdstk.Label("Southwest", (-2, -1), "sw")
+
Note that this example can only be correctly displayed in browsers +with good support for text alignment in SVG images.
+Notes
+Label anchor and transformations (rotation, magnification and +reflection) are not supported by the OASIS format.
+Methods
++ | Create new labels based on this object's |
+
|
+Create a copy this label. |
+
|
+Delete a GDSII property of this element. |
+
|
+Delete the first property of this element matching a name. |
+
|
+Return a GDSII property of this element. |
+
|
+Return the values of the first property of this element matching a name. |
+
|
+Set a GDSII property for this element. |
+
|
+Set a property for this element. |
+
Attributes
++ | Label anchor. |
+
+ | Label layer. |
+
+ | Label scaling factor. |
+
+ | Label origin. |
+
+ | Properties of this element. |
+
+ | Repetition associated with this element. |
+
+ | Label rotation angle. |
+
+ | Label text. |
+
+ | Label text type. |
+
+ | Label reflection across the x axis. |
+
Label anchor.
+Create new labels based on this object’s repetition
attribute.
After the repetition is applied, the original attribute is set to
+None
.
Newly created objects.
+Create a copy this label.
+Copy of this label.
+Delete a GDSII property of this element.
+attr (number) – Property number.
+Delete the first property of this element matching a name.
+name (str) – Property name.
+Return a GDSII property of this element.
+attr (number) – Property number.
+Property value. If the property number does not exist,
+None
is returned.
str or None
+Return the values of the first property of this element matching a name.
+name (str) – Property name.
+List of property values. If no property is found,
+None
is returned.
list or None
+Label layer.
+Label scaling factor.
+Label origin.
+Properties of this element.
+Properties are represented as a list of lists, each containing the +property name followed by its values.
+Repetition associated with this element.
+Label rotation angle.
+Set a GDSII property for this element.
+GDSII properties are stored under the special name “S_GDS_PROPERTY”, as +defined by the OASIS specification.
+attr (number) – Property number.
value (str) – Property value.
Set a property for this element.
+The property name does not have to be unique. Multiple properties can +have the same name.
+name (str) – Property name.
value (str, bytes, number, or sequence of those) – Values associated +with the property.
Notes
+These properties can be used to associate custom metadata with an +element, but general properties are not supported by GDSII files, +only OASIS. Use the specific methods to access GDSII properties.
+Label text.
+Label text type.
+Label reflection across the x axis.
+GDSII/OASIS library.
+The library is a container for multiple cells which keeps track of the +units used to generate the design.
+name – Library name.
unit – User units in meters.
precision – Desired precision to store the units once written to a +GDSII/OASIS file.
Examples
+>>> lib = gdstk.read_gds("filename.gds")
+>>> num_cells = len(lib)
+>>> cell = lib["CELL_NAME"]
+
Notes
+Although cells can be accessed by name this is not a cached +operation. If many queries are expected, using a cell dictionary +is more efficient, for example: +>>> cells = {c.name for c in lib.cells} +>>> cells[“CELL_NAME”]
+See also
+ +Methods
+
|
+Add cells to this library. |
+
|
+Delete the first property of this element matching a name. |
+
|
+Return the values of the first property of this element matching a name. |
+
+ | Return a set of tuples with the layer and data types in the library. |
+
+ | Return a set of tuples with the layer and text types in the library. |
+
|
+Create a new cell and add it to this library. |
+
|
+Remap layers and data/text types for all elements in this library. |
+
|
+Remove cells from this library. |
+
|
+Rename a cell in this library, updating any references that use the old name with the new one. |
+
|
+Add cells to this library, replacing any cells with the same name. |
+
|
+Set a property for this element. |
+
+ | Return the top-level cells in the library. |
+
|
+Save this library to a GDSII file. |
+
|
+Save this library to an OASIS file. |
+
Attributes
++ | List of library cells. |
+
+ | Library name. |
+
+ | Library precision. |
+
+ | Properties of this element. |
+
+ | Library unit. |
+
Add cells to this library.
+Examples
+>>> polygon = gdstk.rectangle((0, 0), (1, 1))
+>>> cell = gdstk.Cell("MAIN")
+>>> cell.add(polygon)
+>>> lib = gdstk.Library()
+>>> lib.add(cell)
+
Notes
+This method does not check whether cell names are duplicated in the +library. If duplicates are found in the library, the resulting file +will be invalid.
+See also
+ +List of library cells.
+Notes
+This attribute is read-only.
+Delete the first property of this element matching a name.
+name (str) – Property name.
+Return the values of the first property of this element matching a name.
+name (str) – Property name.
+List of property values. If no property is found,
+None
is returned.
list or None
+Return a set of tuples with the layer and data types in the library.
+Return a set of tuples with the layer and text types in the library.
+Library name.
+Create a new cell and add it to this library.
+name (str) – Name of the new cell.
+Examples
+>>> lib = gdstk.Library()
+>>> cell = lib.new_cell("MAIN")
+>>> polygon = gdstk.rectangle((0, 0), (1, 1))
+>>> cell.add(polygon)
+
Library precision.
+Properties of this element.
+Properties are represented as a list of lists, each containing the +property name followed by its values.
+Remap layers and data/text types for all elements in this library.
+layer_type_map – Dictionary mapping existing (layer, type) tuples to +desired (layer, type) tuples.
+Remove cells from this library.
+Rename a cell in this library, updating any references that use the old +name with the new one.
+old_name (str or Cell) – Cell or name of the cell to be renamed.
new_name (str) – New cell name.
Add cells to this library, replacing any cells with the same name.
+References to any removed cells are also replaced with the new cell.
+Examples
+>>> polygon = gdstk.rectangle((-10, -10), (10, 10))
+>>> cell = gdstk.Cell("Alignment Mark")
+>>> cell.add(polygon)
+>>> lib = gdstk.read_gds("layout.gds")
+>>> lib.replace(cell)
+
Set a property for this element.
+The property name does not have to be unique. Multiple properties can +have the same name.
+name (str) – Property name.
value (str, bytes, number, or sequence of those) – Values associated +with the property.
Notes
+These properties can be used to associate custom metadata with an +element, but general properties are not supported by GDSII files, +only OASIS. Use the specific methods to access GDSII properties.
+Return the top-level cells in the library.
+Top-level cells are cells that do not appear as dependency of any other +cells in the library.
+Library unit.
+Save this library to a GDSII file.
+outfile (str or pathlib.Path) – Name of the output file.
max_points – Maximal number of vertices per polygon. Polygons with +more vertices that this are automatically fractured.
timestamp (datetime object) – Timestamp to be stored in the GDSII
+file. If None
, the current time is used.
See also
+ +Save this library to an OASIS file.
+outfile (str or pathlib.Path) – Name of the output file.
compression_level – Level of compression for cells (between 0 and 9). +Setting to 0 will disable cell compression, 1 gives the best speed +and 9, the best compression.
detect_rectangles – Store rectangles in compressed format.
detect_trapezoids – Store trapezoids in compressed format.
circle_tolerance – Tolerance for detecting circles. If less or equal +to 0, no detection is performed. Circles are stored in compressed +format.
validation ("crc32", "checksum32", None) – type of validation to +include in the saved file.
standard_properties – Store standard OASIS properties in the file.
Notes
+The standard OASIS options include the maximal string length and +integer size used in the file, maximal numbers of polygon and path +vertices, cell bounding boxes and cell offsets inside the file. +Some of these properties are computationally expensive to calculate. +Use only when required.
+See also
+ +A Cell structure that is stored in binary GDSII format.
+Raw cells are not meant to be created in the same way cells are. They
+come from gdstk.read_rawcells()
and are used when the imported
+elements will not be modified in any way. They are smaller in memory and
+require almost no processing to be used, making them a preferred option
+when using an external GDSII file as a parts library (such as in PDKs).
Notes
+The units of the imported RawCell
are not checked against the
+current user units. It is up to the user to ensure they are equal.
See also
+ +Methods
+
|
+List of raw cells that are referenced by this raw cell. |
+
Attributes
++ | Raw cell name. |
+
+ | Raw cell size (in bytes). |
+
List of raw cells that are referenced by this raw cell.
+recursive – If True
, includes all dependencies recursively.
Raw cell name.
+Notes
+This attribute is read-only.
+Raw cell size (in bytes).
+Notes
+This attribute is read-only.
+Reference to another cell.
+References are used to instance repetitive geometries across different +cells, optionally applying a transformation.
+cell (gdstk.Cell, gdstk.RawCell, str) – referenced cell.
origin – Insertion position of the referenced cell.
rotation – Cell rotation angle (in radians).
magnification – Geometry scaling for this reference.
x_reflection – If True
, the geometry is reflected across the
+horizontal axis before rotation.
columns – Number of repetitions along the first direction.
rows – Number of repetitions along the second direction.
spacing – Spacing between insertions points of repetitions.
Examples
+>>> polygons = gdstk.text("F", 10, (0, 0))
+>>> f_cell = gdstk.Cell("F_CELL")
+>>> f_cell.add(*polygons)
+>>> ref = gdstk.Reference(f_cell, rotation=numpy.pi / 2)
+>>> array_ref = gdstk.Reference(f_cell, columns=3, rows=2,
+... spacing=(8, 10))
+
Notes
+If cell
is a str (cell name), operations on the referenced cell
+are not performed, such as computing the bounding box.
When spacing
is set, a repetition is defined according to the
+rotation
and x_reflection
arguments.
Methods
++ | Create new references based on this object's |
+
+ | Calculate the bounding box of this reference. |
+
+ | Calculate the convex hull of this reference. |
+
|
+Create a copy this reference. |
+
|
+Delete a GDSII property of this element. |
+
|
+Delete the first property of this element matching a name. |
+
|
+Return a GDSII property of this element. |
+
|
+Return a copy of all labels created by this reference. |
+
|
+Return a copy of all paths created by this reference. |
+
|
+Return a copy of all polygons created by this reference. |
+
|
+Return the values of the first property of this element matching a name. |
+
|
+Set a GDSII property for this element. |
+
|
+Set a property for this element. |
+
Attributes
++ | Cell referenced by this object. |
+
+ | Name of the cell referenced by this object. |
+
+ | Reference magnification. |
+
+ | Reference origin. |
+
+ | Properties of this element. |
+
+ | Repetition associated with this element. |
+
+ | Reference rotation angle (in radians). |
+
+ | Reference reflection across the x axis. |
+
Create new references based on this object’s repetition
attribute.
After the repetition is applied, the original attribute is set to
+None
.
Newly created objects.
+Calculate the bounding box of this reference.
+The lower-left and upper-right corners of the bounding box of the
+polygon: ((min_x, min_y), (max_x, max_y))
.
Notes
+This operation can be slow because the precise bounding box +calculation requires recursively querying the polygonal +representations of all shapes created by this reference.
+Examples
+>>> polygons = gdstk.text("F", 10, (0, 0))
+>>> f_cell = gdstk.Cell("F_CELL")
+>>> f_cell.add(*polygons)
+>>> ang = numpy.pi / 4
+>>> array_ref = gdstk.Reference(f_cell, rotation=ang)
+>>> array_ref.repetition = gdstk.Repetition(columns=3, rows=2,
+... v1=8 * numpy.exp(1j * ang), v2=10j * numpy.exp(1j * ang))
+>>> array_ref = gdstk.Reference(f_cell, rotation=numpy.pi / 4)
+>>> array_ref.repetition = gdstk.Repetition(columns=3, rows=2,
+... spacing=(8, 10))
+>>> bbox = array_ref.bounding_box()
+>>> print(bbox)
+((-12.816310409006173, 1.7677669529663689),
+ (11.313708498984761, 27.66555281392367))
+>>> polygon_bb = gdstk.rectangle(*bbox, datatype=1)
+
Cell referenced by this object.
+Name of the cell referenced by this object.
+Calculate the convex hull of this reference.
+The convex hull is the smallest convex polygon that contains all +elements of this reference.
+Vertices of the convex hull.
+Examples
+>>> polygons = gdstk.text("F", 10, (0, 0))
+>>> f_cell = gdstk.Cell("F_CELL")
+>>> f_cell.add(*polygons)
+>>> ang = numpy.pi / 4
+>>> array_ref = gdstk.Reference(f_cell, rotation=ang)
+>>> array_ref.repetition = gdstk.Repetition(columns=3, rows=2,
+... v1=8 * numpy.exp(1j * ang), v2=10j * numpy.exp(1j * ang))
+>>> path = gdstk.FlexPath([(-5, 0), (0, -5), (5, 0)], 1,
+... simple_path=True)
+>>> main_cell = gdstk.Cell("MAIN")
+>>> main_cell.add(array_ref, path)
+>>> hull = main_cell.convex_hull()
+>>> polygon_hull = gdstk.Polygon(hull, datatype=1)
+
Notes
+This operation can be slow because all paths and references are +included in the computation.
+Create a copy this reference.
+Copy of this reference.
+Delete a GDSII property of this element.
+attr (number) – Property number.
+Delete the first property of this element matching a name.
+name (str) – Property name.
+Return a GDSII property of this element.
+attr (number) – Property number.
+Property value. If the property number does not exist,
+None
is returned.
str or None
+Return a copy of all labels created by this reference.
+apply_repetitions – Define whether repetitions should be applied in +the created labels.
depth – If non negative, indicates the number of reference levels
+processed recursively. A value of 0 will result in no references
+being visited. A value of None
(the default) or a negative
+integer will include all reference levels below the cell.
layer – If set, only labels in the defined layer and text type are +returned.
texttype – If set, only labels in the defined layer and text type +are returned.
Notes
+Arguments layer
and texttype
must both be set to integers
+for the filtering to be executed. If either one is None
they
+are both ignored.
Return a copy of all paths created by this reference.
+apply_repetitions – Define whether repetitions should be applied in +the created paths.
depth – If non negative, indicates the number of reference levels
+processed recursively. A value of 0 will result in no references
+being visited. A value of None
(the default) or a negative
+integer will include all reference levels below the cell.
layer – If set, only paths in the defined layer and data type are +returned.
datatype – If set, only paths in the defined layer and data type are +returned.
Notes
+Arguments layer
and datatype
must both be set to integers
+for the filtering to be executed. If either one is None
they
+are both ignored.
Return a copy of all polygons created by this reference.
+apply_repetitions – Define whether repetitions should be applied in +the created polygons.
include_paths – If True
, polygonal representation of paths are
+also included in the result.
depth – If non negative, indicates the number of reference levels
+processed recursively. A value of 0 will result in no references
+being visited. A value of None
(the default) or a negative
+integer will include all reference levels below the cell.
layer – If set, only polygons in the defined layer and data type are +returned.
datatype – If set, only polygons in the defined layer and data type +are returned.
Notes
+Arguments layer
and datatype
must both be set to integers
+for the filtering to be executed. If either one is None
they
+are both ignored.
Return the values of the first property of this element matching a name.
+name (str) – Property name.
+List of property values. If no property is found,
+None
is returned.
list or None
+Reference magnification.
+Reference origin.
+Properties of this element.
+Properties are represented as a list of lists, each containing the +property name followed by its values.
+Repetition associated with this element.
+Reference rotation angle (in radians).
+Set a GDSII property for this element.
+GDSII properties are stored under the special name “S_GDS_PROPERTY”, as +defined by the OASIS specification.
+attr (number) – Property number.
value (str) – Property value.
Set a property for this element.
+The property name does not have to be unique. Multiple properties can +have the same name.
+name (str) – Property name.
value (str, bytes, number, or sequence of those) – Values associated +with the property.
Notes
+These properties can be used to associate custom metadata with an +element, but general properties are not supported by GDSII files, +only OASIS. Use the specific methods to access GDSII properties.
+Reference reflection across the x axis.
+Gather information from a GDSII file without loading the geometry.
+infile (str or pathlib.Path) – Name of the input file.
+Dictionary with library info.
+The following keys are available in the return dictionary:
+cell_names
: list with all cell names
layers_and_datatypes
: set of 2-tuples with layers and data
+types
layers_and_texttypes
: set of 2-tuples with layers and text
+types
num_polygons
: number of polygons in the library
num_paths
: number of paths in the library
num_references
: number of references in the library
num_labels
: number of labels in the library
unit
: library unit
precision
library precision
Check the validation signature of an OASIS file, if any.
+infile (str or pathlib.Path) – Name of the input file.
+Validation result (True/False) and the calculated signature. If the +file does not have a signature, returns (None, 0)
+Import a library from a GDSII stream file.
+infile (str or pathlib.Path) – Name of the input file.
unit (number) – If greater than zero, convert the imported geometry +to the this unit.
tolerance (number) – Default tolerance for loaded paths. If zero or +negative, the library rounding size is used (precision / unit).
filter (iterable of tuples) – If not None
, only shapes with
+layer and data type in the iterable are read.
The imported library.
+Examples
+>>> library = gdstk.read_gds("layout.gds")
+>>> top_cells = library.top_level()
+>>> filtered_lib = gdstk.read_gds("layout.gds", filter={(0, 1)})
+
Import a library from an OASIS stream file.
+infile (str or pathlib.Path) – Name of the input file.
unit (number) – If greater than zero, convert the imported geometry +to the this unit.
tolerance (number) – Default tolerance for loaded paths and round +shapes. If zero or negative, the library rounding size is used +(precision / unit).
The imported library.
+Examples
+>>> library = gdstk.read_oas("layout.oas")
+>>> top_cells = library.top_level()
+
Load cells form a GDSII file without decoding them.
+infile (str or pathlib.Path) – Name of the input file.
+Dictionary of gdstk.RawCell
indexed by name.
Examples
+>>> cell1 = gdstk.Cell("CELL_1")
+>>> cell1.add(gdstk.rectangle((0, 0), (2, 1)))
+>>> cell2 = gdstk.Cell("CELL_2")
+>>> cell2.add(gdstk.Reference(cell1, (-1, 0)))
+>>> library = gdstk.Library()
+>>> library.add(cell1, cell2)
+>>> library.write_gds("test.gds")
+>>> raw_cells = gdstk.read_rawcells("test.gds")
+>>> print(raw_cells.keys())
+dict_keys(['CELL_2', 'CELL_1'])
+>>> print(len(raw_cells["CELL_1"].dependencies(True)))
+0
+>>> print(len(raw_cells["CELL_2"].dependencies(True)))
+1
+>>> deps = raw_cells["CELL_2"].dependencies(True)
+>>> print(deps[0] is raw_cells["CELL_1"])
+True
+
This reference covers only basic aspects of the C++ interface of Gdstk. The +user of the library is expected to go through the examples in this +documentation and to inspect the source code for further details.
+The library is fully contained in the gdstk
namespace. Structs and enums
+use PascalCase
, functions and variables (including constants),
+snake_case
, and definitions are all UPPER_CASE
. Exceptions are the
+members of enums GdsiiRecord
and OasisRecord
, which use UPPER_CASE
+to better match their specification documents.
The library uses only a minimal set of C++ features, namely, limited use of +templates and overloading, no constructors or destructors, no virtual functions +or inheritance, limited private members, and no use of the STL.
+Important
+The user is responsible for the consistent initialization of variables,
+specially struct members. In general, zeroing the whole struct is enough,
+with a few exceptions: Library.name
, Cell.name
, Label.text
, and
+Reference.cell
, Reference.rawcell
, or Reference.name
, depending
+on the reference type. Curve
, FlexPath
, and RobustPath
require
+setting up a few variables to be in a consistent state.
Other struct members can be zero, but probably shouldn’t:
+Reference.magnification
, Label.magnification
, Curve.tolerance
,
+FlexPath.spine.tolerance
, RobustPath.tolerance
,
+RobustPath.max_evals
, RobustPath.width_scale
,
+RobustPath.offset_scale
, and RobustPath.trafo
.
Safe initialization of those structs and members are taken care of in their
+respective init
methods. The user can do it manually, of course, but
+the following idiom is safe to use:
StructName var = {}; // zero-initialize the struct
+var.init(…); // initialize important members
+
Dynamic memory management in Gdstk is realized through 4 functions:
+void* allocate(uint64_t size)
: equivalent to malloc(size)
void* reallocate(void* ptr, uint64_t size)
: equivalent to realloc(ptr,
+1, size)
void* allocate_clear(uint64_t size)
: equivalent to calloc(size)
void free_allocation(void* ptr)
: equivalent to free(ptr)
These can be freely replaced in allocator.h
and allocator.cpp
+with user-defined functions. Their default implementations are just calls to
+their libc equivalents.
The user is required to use these same functions for any structures that might +be reallocated of freed by Gdstk. In the examples in Getting Started +and How-Tos, static allocations are used when we are certain the library +will not modify those, for example, when creating a polygon that will not be +transformed in any way.
+Gdstk is not thread-safe. That said, the library does not use global +variables, so it is safe to use threads as long as they do not modify the same +data structures.
+Classes and functions for construction and manipulation of geometric objects.
+Classes
+
|
+Polygonal geometric object. |
+
|
+Curve object for construction of complex polygons. |
+
|
+Flexible path creation. |
+
|
+Robust path creation. |
+
|
+Repetition object that creates multiple identical elements. |
+
Functions
+
|
+Create a rectangle. |
+
|
+Create a cross shape. |
+
|
+Create a regular polygon. |
+
|
+Create an ellipse, circle, slice or ring. |
+
|
+Create a racetrack shape. |
+
|
+Create polygonal text. |
+
|
+Extract polygonal contours from 2-d array data at given level. |
+
|
+Dilate or erode polygons. |
+
|
+Execute boolean (clipping) operations between polygons. |
+
|
+Slice polygons along x and y axes. |
+
|
+Check whether each point is inside the set of polygons. |
+
|
+Check whether all points are inside the set of polygons. |
+
|
+Check whether any of the points are inside the set of polygons. |
+
Classes and functions used to create and organize the library in a GDSII/OASIS +file.
+Classes
+
|
+Text objects with out geometrical information. |
+
|
+Reference to another cell. |
+
|
+Cell structure. |
+
|
+A Cell structure that is stored in binary GDSII format. |
+
|
+GDSII/OASIS library. |
+
|
+Multi-step GDSII stream file writer. |
+
Functions
+
|
+Import a library from a GDSII stream file. |
+
|
+Import a library from an OASIS stream file. |
+
|
+Load cells form a GDSII file without decoding them. |
+
|
+Read the unit and precision of a GDSII file. |
+
|
+Gather information from a GDSII file without loading the geometry. |
+
|
+Read the precision of an OASIS file. |
+
|
+Check the validation signature of an OASIS file, if any. |
+