# Examples

## Tutorial

The following code is a sample to quickly demonstrate some features of gdspy.

`Download sample file`

      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     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
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    ######################################################################
    #                                                                    #
    #  Copyright 2009-2017 Lucas Heitzmann Gabrielli                     #
    #                                                                    #
    #  This file is part of gdspy.                                       #
    #                                                                    #
    #  gdspy is free software: you can redistribute it and/or modify it  #
    #  under the terms of the GNU General Public License as published    #
    #  by the Free Software Foundation, either version 3 of the          #
    #  License, or any later version.                                    #
    #                                                                    #
    #  gdspy is distributed in the hope that it will be useful, but      #
    #  WITHOUT ANY WARRANTY; without even the implied warranty of        #
    #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the     #
    #  GNU General Public License for more details.                      #
    #                                                                    #
    #  You should have received a copy of the GNU General Public         #
    #  License along with gdspy.  If not, see                            #
    #  <http://www.gnu.org/licenses/>.                                   #
    #                                                                    #
    ######################################################################

    import numpy
    import gdspy

    print('Using gdspy module version ' + gdspy.__version__)

    # ------------------------------------------------------------------ #
    #      POLYGONS
    # ------------------------------------------------------------------ #

    # First we need a cell to add the polygons to.
    poly_cell = gdspy.Cell('POLYGONS')

    # We define the polygon through its vertices.
    points = [(0, 0), (2, 2), (2, 6), (-6, 6), (-6, -6), (-4, -4), (-4, 4),
              (0, 4)]

    # Create the polygon on layer 1.
    poly1 = gdspy.Polygon(points, 1)

    # Add the new polygon to the cell.
    poly_cell.add(poly1)

    # Create another polygon from the same set of points, but rotate it
    # 180 degrees and add it to the cell.
    poly2 = gdspy.Polygon(points, 1).rotate(numpy.pi)
    poly_cell.add(poly2)

    # To create rectangles we don't need to give the 4 corners, only 2.
    # Note that we don't need to create a variable if we are not going to
    # use it, just add the rectangle directly to the cell.  Create a
    # rectangle in layer 2.
    poly_cell.add(gdspy.Rectangle((18, 1), (22, 2), 2))

    # There are no circles in the GDSII specification, so rounded shapes
    # are actually many-sided polygons.  Create a circle in layer 2,
    # centered at (27, 2), and with radius 2.
    poly_cell.add(gdspy.Round((27, 2), 2, layer=2))

    # The Round class is quite versatile: it provides circles, pie slices,
    # rings and ring sections, like this one in layer 2.
    poly_cell.add(gdspy.Round((23.5, 7), 15, inner_radius=14,
                              initial_angle=-2.0 * numpy.pi / 3.0,
                              final_angle=-numpy.pi / 3.0, layer=2))

    # ------------------------------------------------------------------ #
    #      PATHS
    # ------------------------------------------------------------------ #

    path_cell = gdspy.Cell('PATHS')

    # Start a path from the origin with width 1.
    path1 = gdspy.Path(1, (0, 0))

    # Add a straight segment to the path in layer 1, datatype 1, with length
    # 3, going in the '+x' direction. Since we'll use this layer/datatype
    # configuration again, we can setup a dict containing this info.
    spec = {'layer': 1, 'datatype': 1}
    path1.segment(3, '+x', **spec)

    # Add a curve to the path by specifying its radius as 2 and its initial
    # and final angles.
    path1.arc(2, -numpy.pi / 2.0, numpy.pi / 6.0, **spec)

    # Add another segment to the path in layer 1, with length 4 and
    # pointing in the direction defined by the last piece we added above.
    path1.segment(4, **spec)

    # Add a curve using the turn command.  We specify the radius 2 and
    # turning angle. The agnle can also be specified with 'l' and 'r' for
    # left and right turns of 90 degrees, or 'll' and 'rr' for 180 degrees.
    path1.turn(2, -2.0 * numpy.pi / 3.0, **spec)

    # Final piece of the path.  Add a straight segment and tapper the path
    # width from the original 1 to 0.5.
    path1.segment(3, final_width=0.5, **spec)
    path_cell.add(path1)

    # We can also create parallel paths simultaneously.  Start 2 paths with
    # width 0.5 each,nd pitch 1, originating where our last path ended.
    path2 = gdspy.Path(0.5, (path1.x, path1.y), number_of_paths=2,
                       distance=1)

    # Add a straight segment to the paths gradually increasing their
    # distance to 1.5, in the direction in which the last path ended.
    spec['layer'] = 2
    path2.segment(3, path1.direction, final_distance=1.5, **spec)

    # Path commands can be concatenated.  Add a turn and a tapper segment
    # in one expression, followed by a final turn.
    path2.turn(2, -2.0 * numpy.pi / 3.0, **spec).segment(4, final_distance=1,
                                                         **spec)
    path2.turn(4, numpy.pi / 6.0, **spec)
    path_cell.add(path2)

    # Create another single path 0.5 wide, starting where the path above
    # ended, and add to it a line segment in the 3rd layer in the '-y'
    # direction.
    path3 = gdspy.Path(0.5, (path2.x, path2.y))
    path3.segment(1, '-y', layer=3)

    # We can create paths based on parametric curves. First we need to
    # define the curve function, with 1 argument.  This argument will vary
    # from 0 to 1 and the return value should be the (x, y) coordinates of
    # the path.  This could be a lambda-expression if the function is
    # simple enough.  We will create a spiral path.  Note that the function
    # returns (0, 0) when t=0, so that our path is connected.
    def spiral(t):
        r = 4 - 3 * t
        theta = 5 * t * numpy.pi
        x = 4 - r * numpy.cos(theta)
        y = -r * numpy.sin(theta)
        return (x, y)

    # We can also create the derivative of the curve to pass to out path
    # path member, otherwise it will be numerically calculated.  In the
    # spiral case we don't want the exact derivative, but the derivative of
    # the spiral as if its radius was constant.  This will ensure that our
    # path is connected at the start (geometric problem of this kind of
    # spiral).
    def dspiral_dt(t):
        theta = 5 * t * numpy.pi
        dx_dt = numpy.sin(theta)
        dy_dt = -numpy.cos(theta)
        return (dx_dt, dy_dt)

    # Add the parametric spiral to the path in layer 3.  Note that we can
    # still tapper the width (linearly or with a taper function).  To make
    # the curve smoother, we increase the number of evaluations of the
    # function (fracture will be performed automatically to ensure polygons
    # with less than 200 points).
    path3.parametric(spiral, dspiral_dt,
                     final_width=lambda t: 0.1 + abs(0.4 * (1 - 2 * t)**3),
                     number_of_evaluations=600, layer=3)
    path_cell.add(path3)

    # Polygonal paths are defined by the points they pass through.  The
    # width of the path can be given as a number, representing the path
    # width along is whole extension, or as a list, where each element is
    # the width of the path at one point.  Our path will have width 0.5 in
    # all points, except the last, where it will tapper up to 1.5.  More
    # than 1 path can be defined in parallel as well (useful for buses).
    # The distance between the paths work the same way as the width: it's
    # either a constant number, or a list.  We create 5 parallel paths that
    # are larger and further apart on the last point. The paths are put in
    # layers 4 and 5. Since we have 5 paths, the list of layers will be
    # run more than once, so the 5 paths will actually be in layers 4, 5, 4,
    # 5, and 4.
    points = [(20, 12), (24, 8), (24, 4), (24, -2)]
    widths = [0.5] * (len(points) - 1) + [1.5]
    distances = [0.8] * (len(points) - 1) + [2.4]
    polypath = gdspy.PolyPath(points, widths, number_of_paths=5,
                              distance=distances, layer=[4, 5])

    # We can round the corners of any Polygon or PolygonSet with the fillet
    # method. Here we use a radius of 0.2.
    # polypath.fillet(0.2)
    path_cell.add(polypath)

    # L1Paths use only segments in 'x' and 'y' directions, useful for some
    # lithography mask writers.  We specify a path composed of 16 segments
    # of length 4.  The turns after each segment can be either 90 degrees
    # CCW (positive) or CW (negative).  The absolute value of the turns
    # produces a scaling of the path width and distance between paths in
    # segments immediately after the turn.
    lengths = [4] * 8
    turns = [-1, -1, 1, 1, -1, -2, 1, 0.5]
    l1path = gdspy.L1Path((-1, -11), '+y', 0.5, lengths, turns,
                          number_of_paths=3, distance=0.7, layer=6)
    path_cell.add(l1path)

    # ------------------------------------------------------------------ #
    #      POLYGON OPERATIONS
    # ------------------------------------------------------------------ #

    # Boolean operations can be executed with either gdspy polygons or
    # point lists).  The operations are union, intersection, subtraction,
    # symmetric subtracion (respectively 'or', 'and', 'not', 'xor').
    oper_cell = gdspy.Cell('OPERATIONS')

    # Here we subtract the previously created spiral from a rectangle with
    # the 'not' operation.
    oper_cell.add(gdspy.fast_boolean(gdspy.Rectangle((10, -4), (17, 4)), path3,
                                     'not', layer=1))

    # Polygon offset (inset and outset) can be used, for instance, to
    # define safety margins around shapes.

    spec = {'layer': 7}
    path4 = gdspy.Path(0.5, (21, -5)).segment(3, '+x', **spec)\
            .turn(4, 'r', **spec).turn(4, 'rr', **spec)\
            .segment(3, **spec)
    oper_cell.add(path4)

    # Merge all parts into a single polygon.
    merged = gdspy.fast_boolean(path4, None, 'or', max_points=0)

    # Offset the path shape by 0.5 and add it to the cell.
    oper_cell.add(gdspy.offset(merged, 1, layer=8))

    # ------------------------------------------------------------------ #
    #      SLICING POLYGONS
    # ------------------------------------------------------------------ #

    # If there is the need to cut a polygon or set of polygons, it's better
    # to use the slice function than set up a boolean operation, since it
    # runs much faster.  Slices are multiple cuts perpendicular to an axis.
    slice_cell = gdspy.Cell('SLICE')
    original = gdspy.Round((0, 0), 10, inner_radius=5)

    # Slice the original ring along x = -7 and x = 7.
    result = gdspy.slice(original, [-7, 7], 0, layer=1)

    # The result is a tuple of polygon sets, one for each slice.  To keep
    # add the region betwen our 2 cuts, we chose result[1].
    slice_cell.add(result[1])

    # If the cut needs to be at an angle we can rotate the geometry, slice
    # it, and rotate back.
    original = gdspy.PolyPath([(12, 0), (12, 8), (28, 8), (28, -8),
                               (12, -8), (12, 0)], 1, 3, 2)
    original.rotate(numpy.pi / 3, center=(20, 0))
    result = gdspy.slice(original, 7, 1, layer=2)
    result[0].rotate(-numpy.pi / 3, center=(20, 0))
    slice_cell.add(result[0])

    # ------------------------------------------------------------------ #
    #      REFERENCES AND TEXT
    # ------------------------------------------------------------------ #

    # Cells can contain references to other cells.
    ref_cell = gdspy.Cell('REFS')
    ref_cell.add(gdspy.CellReference(poly_cell, (0, 30), x_reflection=True))
    ref_cell.add(gdspy.CellReference(poly_cell, (25, 0), rotation=180))

    # References can be whole arrays. Add an array of the operations cell
    # with 2 lines and 3 columns and 1st element at (25, 10).
    ref_cell.add(gdspy.CellArray('OPERATIONS', 3, 2, (35, 30), (25, 10),
                                 magnification=1.5))

    # Text are also sets of polygons. They have edges parallel to 'x' and
    # 'y' only.
    ref_cell.add(gdspy.Text('Created with gsdpy ' + gdspy.__version__, 7,
                            (-7, -35), layer=6))

    # Labels are special text objects which don't define any actual
    # geometry, but can be used to annotate the drawing.  Rotation,
    # magnification and reflection of the text are not supported by the
    # included GUI, but they are included in the resulting GDSII file.
    ref_cell.add(gdspy.Label('Created with gdspy ' + gdspy.__version__,
                             (-7, -36), 'nw', layer=6))

    # ------------------------------------------------------------------ #
    #      Translation
    # ------------------------------------------------------------------ #

    trans_cell = gdspy.Cell('TRANS')

    # Any geometric object can be translated by providing the distance to
    # translate in the x-direction and y-direction:  translate(dx, dy)
    rect1 = gdspy.Rectangle((80, 0), (81, 1), 1)
    rect1.translate(2, 0)
    trans_cell.add(rect1)

    # Translatable objects can also be copied & translated in the same way.
    rect2 = gdspy.Rectangle((80, 0), (81, 1), 2)
    rect3 = gdspy.copy(rect2, 0, 3)
    trans_cell.add(rect2)
    trans_cell.add(rect3)

    # Reference Cells are also translatable, and thus copyable.
    ref1 = gdspy.CellReference(poly_cell, (25, 0), rotation=180)
    ref2 = gdspy.copy(ref1, 30, 30)
    trans_cell.add(ref1)
    trans_cell.add(ref2)

    # Same goes for Labels & Text
    text1 = gdspy.Text('Created with gsdpy ' + gdspy.__version__, 7,
                       (-7, -35), layer=6)
    text2 = gdspy.copy(text1, 0, -20)
    label1 = gdspy.Label('Created with gdspy ' + gdspy.__version__,
                         (-7, -36), 'nw', layer=6)
    label2 = gdspy.copy(label1, 0, -20)
    trans_cell.add(text1)
    trans_cell.add(text2)
    trans_cell.add(label1)
    trans_cell.add(label2)

    # ------------------------------------------------------------------ #
    #      OUTPUT
    # ------------------------------------------------------------------ #

    # Output the layout to a GDSII file (default to all created cells).
    # Set the units we used to micrometers and the precision to nanometers.
    gdspy.write_gds('tutorial.gds', unit=1.0e-6, precision=1.0e-9)

    # ------------------------------------------------------------------ #
    #      IMPORT
    # ------------------------------------------------------------------ #

    # Import the file we just created, and extract the cell 'POLYGONS'. To
    # avoid naming conflict, we will rename all cells.
    gdsii = gdspy.GdsLibrary()
    gdsii.read_gds('tutorial.gds',
                   rename={'POLYGONS': 'IMPORT_POLY',
                           'PATHS': 'IMPORT_PATHS',
                           'OPERATIONS': 'IMPORT_OPER',
                           'SLICE': 'IMPORT_SLICE',
                           'REFS': 'IMPORT_REFS',
                           'TRANS': 'IMPORT_TRANS'},
                   layers={1: 7, 2: 8, 3: 9})

    # Now we extract the cells we want to actually include in our current
    # structure. Note that the referenced cells will be automatically
    # extracted as well.
    gdsii.extract('IMPORT_REFS')

    # ------------------------------------------------------------------ #
    #      VIEWER
    # ------------------------------------------------------------------ #

    # View the layout using a GUI.  Full description of the controls can
    # be found in the online help at http://gdspy.sourceforge.net/
    gdspy.LayoutViewer()

## Integrated Photonics

This example demonstrates the use of gdspy primitives to create more complex structures.

These structures are commonly used in the field of integrated photonics.

`Download sample file`

      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     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
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    ######################################################################
    #                                                                    #
    #  Copyright 2009-2017 Lucas Heitzmann Gabrielli                     #
    #                                                                    #
    #  This file is part of gdspy.                                       #
    #                                                                    #
    #  gdspy is free software: you can redistribute it and/or modify it  #
    #  under the terms of the GNU General Public License as published    #
    #  by the Free Software Foundation, either version 3 of the          #
    #  License, or any later version.                                    #
    #                                                                    #
    #  gdspy is distributed in the hope that it will be useful, but      #
    #  WITHOUT ANY WARRANTY; without even the implied warranty of        #
    #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the     #
    #  GNU General Public License for more details.                      #
    #                                                                    #
    #  You should have received a copy of the GNU General Public         #
    #  License along with gdspy.  If not, see                            #
    #  <http://www.gnu.org/licenses/>.                                   #
    #                                                                    #
    ######################################################################

    import numpy
    import gdspy

    def waveguide(path, points, finish, bend_radius, number_of_points=0.01,
                  direction=None, layer=0, datatype=0):
        '''
        Easy waveguide creation tool with absolute positioning.

        path             : `gdspy.Path` which servers as base for the waveguide
        points           : coordinates along which the waveguide will travel
        finish           : end point of the waveguide
        bend_radius      : radius of the turns in the waveguide
        number_of_points : same as in `path.turn`
        direction        : starting direction
        layer            : GDSII layer number
        datatype         : GDSII datatype number

        Return `path`.
        '''
        if direction is not None:
            path.direction = direction
        axis = 0 if path.direction[1] == 'x' else 1
        points.append(finish[(axis + len(points)) % 2])
        n = len(points)
        if points[0] > (path.x, path.y)[axis]:
            path.direction = ['+x', '+y'][axis]
        else:
            path.direction = ['-x', '-y'][axis]
        for i in range(n):
            path.segment(abs(points[i] - (path.x, path.y)[axis]) - bend_radius,
                         layer=layer, datatype=datatype)
            axis = 1 - axis
            if i < n - 1:
                goto = points[i + 1]
            else:
                goto = finish[axis]
            if (goto > (path.x, path.y)[axis]) ^ ((path.direction[0] == '+') ^
                                                  (path.direction[1] == 'x')):
                bend = 'l'
            else:
                bend = 'r'
            path.turn(bend_radius, bend, number_of_points=number_of_points,
                      layer=layer, datatype=datatype)
        return path.segment(abs(finish[axis] - (path.x, path.y)[axis]),
                            layer=layer, datatype=datatype)

    def taper(path, length, final_width, final_distance, direction=None,
              layer=0, datatype=0):
        '''
        Linear tapers for the lazy.

        path        : `gdspy.Path` to append the taper
        length      : total length
        final_width : final width of th taper
        direction   : taper direction
        layer       : GDSII layer number (int or list)
        datatype    : GDSII datatype number (int or list)

        Parameters `layer` and `datatype` must be of the same type. If they are
        lists, they must have the same length. Their length indicate the number of
        pieces that compose the taper.

        Return `path`.
        '''
        if layer.__class__ == datatype.__class__ == [].__class__:
            assert len(layer) == len(datatype)
        elif isinstance(layer, int) and isinstance(datatype, int):
            layer = [layer]
            datatype = [datatype]
        else:
            raise ValueError('Parameters layer and datatype must have the same '
                             'type (either int or list) and length.')
        n = len(layer)
        w = numpy.linspace(2 * path.w, final_width, n + 1)[1:]
        d = numpy.linspace(path.distance, final_distance, n + 1)[1:]
        l = float(length) / n
        for i in range(n):
            path.segment(l, direction, w[i], d[i], layer=layer[i],
                         datatype=datatype[i])
        return path

    def grating(period, number_of_teeth, fill_frac, width, position, direction,
                lda=1, sin_theta=0, focus_distance=-1, focus_width=-1,
                evaluations=99, layer=0, datatype=0):
        '''
        Straight or focusing grating.

        period          : grating period
        number_of_teeth : number of teeth in the grating
        fill_frac       : filling fraction of the teeth with respect to the period
        width           : width of the grating
        position        : grating position (feed point)
        direction       : one of {'+x', '-x', '+y', '-y'}
        lda             : free-space wavelength
        sin_theta       : sine of incidence angle
        focus_distance  : focus distance (negative for straight grating)
        focus_width     : if non-negative, the focusing area is included in the
                          result (usually for negative resists) and this is the
                          width of the waveguide connecting to the grating
        evaluations     : number of parametric evaluations of `path.parametric`
        layer           : GDSII layer number
        datatype        : GDSII datatype number

        Return `PolygonSet`
        '''
        if focus_distance < 0:
            path = gdspy.L1Path(
                (position[0] - 0.5 * width,
                 position[1] + 0.5 * (number_of_teeth - 1 + fill_frac) * period),
                '+x', period * fill_frac, [width], [], number_of_teeth,
                period, layer=layer, datatype=datatype)
        else:
            neff = lda / float(period) + sin_theta
            qmin = int(focus_distance / float(period) + 0.5)
            path = gdspy.Path(period * fill_frac, position)
            max_points = 199 if focus_width < 0 else 2*evaluations
            c3 = neff**2 - sin_theta**2
            w = 0.5*width
            for q in range(qmin, qmin + number_of_teeth):
                c1 = q * lda * sin_theta
                c2 = (q * lda) ** 2
                path.parametric(
                    lambda t: (width*t - w,
                               (c1 + neff*numpy.sqrt(c2 - c3*(width*t-w)**2))/c3),
                    number_of_evaluations=evaluations, max_points=max_points,
                    layer=layer, datatype=datatype)
                path.x = position[0]
                path.y = position[1]
            if focus_width >= 0:
                path.polygons[0] = numpy.vstack(
                    (path.polygons[0][:evaluations,:],
                     ([position] if focus_width == 0 else
                      [(position[0] + 0.5*focus_width, position[1]),
                       (position[0] - 0.5*focus_width, position[1])])))
                path.fracture()
        if direction == '-x':
            return path.rotate(0.5*numpy.pi, position)
        elif direction == '+x':
            return path.rotate(-0.5*numpy.pi, position)
        elif direction == '-y':
            return path.rotate(numpy.pi, position)
        else:
            return path

    if __name__ == '__main__':
        ## Examples

        c1 = gdspy.Cell('Example1')
        ## Waveguide starts at (0, 0)...
        path = gdspy.Path(0.450, (0, 0))
        ## ... then starts in the +y direction up to y=200, then through x=500,
        ## and stops at (800, 400). All bends have radius 50.
        waveguide(path, [200, 500], (800, 400), 50, direction='+y')
        c1.add(path)

        ## More useful example including a taper and a grating:
        c2 = gdspy.Cell('Example2')
        for i in range(3):
            path = gdspy.Path(0.120, (50 * i, 0))
            taper(path, 75, 0.450, 0, '+y', layer=list(range(5, 1, -1)),
                  datatype=list(range(1, 5)))
            waveguide(path, [300 - 20 * i], (500 + 50 * i, 425), 50, layer=1,
                      datatype=1)
            c2.add(path)
            c2.add(grating(0.626, 28, 0.5, 19, (path.x, path.y), path.direction,
                           1.55, numpy.sin(numpy.pi*8/180), 21.5, 2*path.w,
                           layer=1, datatype=1))

        ## Straight grating and positive resist example
        c3 = gdspy.Cell('Example3')
        spec = {'layer':4, 'datatype':3}
        lda = 1.55
        gr_width = 10
        gr_per = 0.626
        gr_teeth = 20
        wg_clad = 4
        wg_width = 0.45
        tp_len = 700
        c3.add(grating(gr_per, gr_teeth, 0.5, gr_width, (gr_per*gr_teeth, 0), '-x',
                       lda, numpy.sin(numpy.pi*8/180), **spec))
        path = gdspy.Path(wg_clad, (0, 0), number_of_paths=2,
                          distance=gr_width+wg_clad)
        path.segment(gr_per*gr_teeth, '+x', **spec)
        taper(path, tp_len, wg_clad, wg_width+wg_clad, **spec)
        waveguide(path, [800], (tp_len+gr_per*gr_teeth, 200), 50, 0.1, **spec)
        taper(path, tp_len, wg_clad, gr_width+wg_clad, **spec)
        c3.add(grating(gr_per, gr_teeth, 0.5, gr_width, (path.x, path.y),
                       path.direction, lda, numpy.sin(numpy.pi*8/180), **spec))
        path.segment(gr_per*gr_teeth, **spec)
        c3.add(path)

        gdspy.LayoutViewer()