# Paths and straights¶

gdsfactory leverages PHIDL efficient module for creating smooth curves, particularly useful for creating straight structures such as those used in photonics. Creating a path component is simple:

Create a blank

`Path`

Append points to the

`Path`

either using the built-in functions (`arc()`

,`straight()`

,`euler()`

, etc) or by providing your own lists of pointsSpecify what you want the cross-section (

`CrossSection`

) to look likeCombine the

`Path`

and the`CrossSection`

(will output a Device with the path polygons in it)

## Path creation¶

The first step is to generate the list of points we want the path to follow. Let’s start out by creating a blank `Path`

and using the built-in functions to make a few smooth turns.

```
[1]:
```

```
import gdsfactory as gf
import numpy as np
P = gf.Path()
P.append(gf.path.arc(radius=10, angle=90)) # Circular arc
P.append(gf.path.straight(length=10)) # Straight section
P.append(gf.path.euler(radius=3, angle=-90)) # Euler bend (aka "racetrack" curve)
P.append(gf.path.straight(length=40))
P.append(gf.path.arc(radius=8, angle=-45))
P.append(gf.path.straight(length=10))
P.append(gf.path.arc(radius=8, angle=45))
P.append(gf.path.straight(length=10))
gf.plot(P)
```

```
2021-09-25 16:19:06.652 | INFO | gdsfactory.config:<module>:51 - 3.2.9
```

```
[2]:
```

```
p2 = P.copy().rotate()
gf.plot([P, p2])
```

```
[3]:
```

```
P.points - p2.points
```

```
[3]:
```

```
array([[ 0.00000000e+00, 0.00000000e+00],
[ 2.59744785e-02, -6.19378670e-02],
[ 5.24914793e-02, -1.23645416e-01],
...,
[ 3.14476949e+01, -4.82149721e+01],
[ 3.14681519e+01, -4.82649827e+01],
[ 3.43970841e+01, -5.53360505e+01]])
```

We can also modify our Path in the same ways as any other PHIDL object:

Manipulation with

`move()`

,`rotate()`

,`mirror()`

, etcAccessing properties like

`xmin`

,`y`

,`center`

,`bbox`

, etc

```
[4]:
```

```
P.movey(10)
P.xmin = 20
gf.plot(P)
```

We can also check the length of the curve with the `length()`

method:

```
[5]:
```

```
P.length()
```

```
[5]:
```

```
107.69901058617913
```

## Defining the cross-section¶

Now that we’ve got our path defined, the next step is to tell phidl what we want the cross-section of the path to look like. To do this, we create a blank `CrossSection`

and add whatever cross-sections we want to it. We can then combine the `Path`

and the `CrossSection`

using the `extrude()`

function to generate our final geometry:

```
[6]:
```

```
# Create a blank CrossSection
X = gf.CrossSection()
# Add a single "section" to the cross-section
X.add(width=1, offset=0, layer=0)
# Combine the Path and the CrossSection
c = gf.path.extrude(P, X)
# Quickplot the resulting Component
c
```

```
[6]:
```

Now, what if we want a more complicated straight? For instance, in some photonic applications it’s helpful to have a shallow etch that appears on either side of the straight (often called a “sleeve). Additionally, it might be nice to have a Port on either end of the center section so we can snap other geometries to it. Let’s try adding something like that in:

```
[7]:
```

```
import gdsfactory as gf
p = gf.path.arc()
# Create a blank CrossSection
X = gf.CrossSection()
# Add a a few "sections" to the cross-section
X.add(width=1, offset=0, layer=0, ports=("in", "out"))
X.add(width=3, offset=2, layer=2)
X.add(width=3, offset=-2, layer=2)
# Combine the Path and the CrossSection
straight = gf.path.extrude(p, cross_section=X)
# Quickplot the resulting Component
straight
```

```
[7]:
```

You can also maintain the port names by passing the `rename_ports=False`

```
[8]:
```

```
import gdsfactory as gf
p = gf.path.arc()
# Create a blank CrossSection
X = gf.CrossSection()
# Add a a few "sections" to the cross-section
X.add(width=1, offset=0, layer=0, ports=("in", "out"))
X.add(width=3, offset=2, layer=2)
X.add(width=3, offset=-2, layer=2)
# Combine the Path and the CrossSection
straight = gf.path.extrude(p, X)
# Quickplot the resulting Component
straight
```

```
[8]:
```

## Building Paths quickly¶

You can pass `append()`

lists of path segments. This makes it easy to combine paths very quickly. Below we show 3 examples using this functionality:

**Example 1:** Assemble a complex path by making a list of Paths and passing it to `append()`

```
[9]:
```

```
P = gf.Path()
# Create the basic Path components
left_turn = gf.path.euler(radius=4, angle=90)
right_turn = gf.path.euler(radius=4, angle=-90)
straight = gf.path.straight(length=10)
# Assemble a complex path by making list of Paths and passing it to `append()`
P.append(
[
straight,
left_turn,
straight,
right_turn,
straight,
straight,
right_turn,
left_turn,
straight,
]
)
gf.plot(P)
```

**Example 2:** Create an “S-turn” just by making a list of `[left_turn, right_turn]`

```
[10]:
```

```
P = gf.Path()
# Create an "S-turn" just by making a list
s_turn = [left_turn, right_turn]
P.append(s_turn)
gf.plot(P)
```

**Example 3:** Repeat the S-turn 3 times by nesting our S-turn list in another list

```
[11]:
```

```
P = gf.Path()
# Create an "S-turn" using a list
s_turn = [left_turn, right_turn]
# Repeat the S-turn 3 times by nesting our S-turn list 3x times in another list
triple_s_turn = [s_turn, s_turn, s_turn]
P.append(triple_s_turn)
gf.plot(P)
```

Note you can also use the Path() constructor to immediately contruct your Path:

```
[12]:
```

```
P = gf.Path([straight, left_turn, straight, right_turn, straight])
gf.plot(P)
```

## Custom curves¶

Now let’s have some fun and try to make a loop-de-loop structure with parallel straights and several Ports.

To create a new type of curve we simply make a function that produces an array of points. The best way to do that is to create a function which allows you to specify a large number of points along that curve – in the case below, the `looploop()`

function outputs 1000 points along a looping path. Later, if we want reduce the number of points in our geometry we can trivially `simplify`

the path.

```
[13]:
```

```
import numpy as np
def looploop(num_pts=1000):
"""Simple limacon looping curve"""
t = np.linspace(-np.pi, 0, num_pts)
r = 20 + 25 * np.sin(t)
x = r * np.cos(t)
y = r * np.sin(t)
points = np.array((x, y)).T
return points
# Create the path points
P = gf.Path()
P.append(gf.path.arc(radius=10, angle=90))
P.append(gf.path.straight())
P.append(gf.path.arc(radius=5, angle=-90))
P.append(looploop(num_pts=1000))
P.rotate(-45)
# Create the crosssection
X = gf.CrossSection()
X.add(width=0.5, offset=2, layer=0)
X.add(width=0.5, offset=4, layer=1)
X.add(width=1.5, offset=0, layer=2, ports=["in", "out"])
X.add(width=1, offset=0, layer=3)
c = gf.path.extrude(P, X)
c
```

```
[13]:
```

You can create Paths from any array of points – just be sure that they form smooth curves! If we examine our path `P`

we can see that all we’ve simply created a long list of points:

```
[14]:
```

```
import numpy as np
path_points = P.points # Curve points are stored as a numpy array in P.points
print(np.shape(path_points)) # The shape of the array is Nx2
print(len(P)) # Equivalently, use len(P) to see how many points are inside
```

```
(1359, 2)
1359
```

## Simplifying / reducing point usage¶

One of the chief concerns of generating smooth curves is that too many points are generated, inflating file sizes and making boolean operations computationally expensive. Fortunately, PHIDL has a fast implementation of the Ramer-Douglas–Peucker algorithm that lets you reduce the number of points in a curve without changing its shape. All that needs to be done is when you made a component `component()`

extruding
the path with a cross_section, you specify the `simplify`

argument.

If we specify `simplify = 1e-3`

, the number of points in the line drops from 12,000 to 4,000, and the remaining points form a line that is identical to within `1e-3`

distance from the original (for the default 1 micron unit size, this corresponds to 1 nanometer resolution):

```
[15]:
```

```
# The remaining points form a identical line to within `1e-3` from the original
c = gf.path.extrude(p=P, cross_section=X, simplify=1e-3)
c
```

```
[15]:
```

Let’s say we need fewer points. We can increase the simplify tolerance by specifying `simplify = 1e-1`

. This drops the number of points to ~400 points form a line that is identical to within `1e-1`

distance from the original:

```
[16]:
```

```
c = gf.path.extrude(P, cross_section=X, simplify=1e-1)
c
```

```
[16]:
```

Taken to absurdity, what happens if we set `simplify = 0.3`

? Once again, the ~200 remaining points form a line that is within `0.3`

units from the original – but that line looks pretty bad.

```
[17]:
```

```
c = gf.path.extrude(P, cross_section=X, simplify=0.3)
c
```

```
[17]:
```

## Curvature calculation¶

The `Path`

class has a `curvature()`

method that computes the curvature `K`

of your smooth path (K = 1/(radius of curvature)). This can be helpful for verifying that your curves transition smoothly such as in track-transition curves (also known as “racetrack”, “Euler”, or “straight-to-bend” curves in the photonics world). Note this curvature is numerically computed so areas where the curvature jumps instantaneously (such as between
an arc and a straight segment) will be slightly interpolated, and sudden changes in point density along the curve can cause discontinuities.

```
[18]:
```

```
import matplotlib.pyplot as plt
import gdsfactory as gf
straight_points = 100
P = gf.Path()
P.append(
[
gf.path.straight(
length=10, npoints=straight_points
), # Should have a curvature of 0
gf.path.euler(
radius=3, angle=90, p=0.5, use_eff=False
), # Euler straight-to-bend transition with min. bend radius of 3 (max curvature of 1/3)
gf.path.straight(
length=10, npoints=straight_points
), # Should have a curvature of 0
gf.path.arc(radius=10, angle=90), # Should have a curvature of 1/10
gf.path.arc(radius=5, angle=-90), # Should have a curvature of -1/5
gf.path.straight(
length=2, npoints=straight_points
), # Should have a curvature of 0
]
)
gf.plot(P)
```

Arc paths are equivalent to `bend_circular`

and euler paths are equivalent to `bend_euler`

```
[19]:
```

```
s, K = P.curvature()
plt.plot(s, K, ".-")
plt.xlabel("Position along curve (arc length)")
plt.ylabel("Curvature")
```

```
[19]:
```

```
Text(0, 0.5, 'Curvature')
```

You can compare two 90 degrees euler bend with 180 euler bend.

A 180 euler bend is shorter, and has less loss than two 90 degrees euler bend.

```
[20]:
```

```
import matplotlib.pyplot as plt
import gdsfactory as gf
straight_points = 100
P = gf.Path()
P.append(
[
gf.path.euler(radius=3, angle=90, p=1, use_eff=False),
gf.path.euler(radius=3, angle=90, p=1, use_eff=False),
gf.path.straight(length=6, npoints=100),
gf.path.euler(radius=3, angle=180, p=1, use_eff=False),
]
)
gf.plot(P)
```

```
[21]:
```

```
s, K = P.curvature()
plt.plot(s, K, ".-")
plt.xlabel("Position along curve (arc length)")
plt.ylabel("Curvature")
```

```
[21]:
```

```
Text(0, 0.5, 'Curvature')
```

## Transitioning between cross-sections¶

Often a critical element of building paths is being able to transition between cross-sections. You can use the `transition()`

function to do exactly this: you simply feed it two `CrossSection`

s and it will output a new `CrossSection`

that smoothly transitions between the two.

Let’s start off by creating two cross-sections we want to transition between. Note we give all the cross-sectional elements names by specifying the `name`

argument in the `add()`

function – this is important because the transition function will try to match names between the two input cross-sections, and any names not present in both inputs will be skipped.

```
[22]:
```

```
import numpy as np
import gdsfactory as gf
# Create our first CrossSection
X1 = gf.CrossSection()
X1.add(width=1.2, offset=0, layer=2, name="wg", ports=("o1", "o2"))
X1.add(width=2.2, offset=0, layer=3, name="etch")
X1.add(width=1.1, offset=3, layer=1, name="wg2")
# Create the second CrossSection that we want to transition to
X2 = gf.CrossSection()
X2.add(width=1, offset=0, layer=2, name="wg", ports=("o1", "o2"))
X2.add(width=3.5, offset=0, layer=3, name="etch")
X2.add(width=3, offset=5, layer=1, name="wg2")
# To show the cross-sections, let's create two Paths and
# create Devices by extruding them
P1 = gf.path.straight(length=5)
P2 = gf.path.straight(length=5)
wg1 = gf.path.extrude(P1, X1)
wg2 = gf.path.extrude(P2, X2)
# Place both cross-section Devices and quickplot them
c = gf.Component()
wg1ref = c << wg1
wg2ref = c << wg2
wg2ref.movex(7.5)
c
```

```
[22]:
```

```
[ ]:
```

```
```

Now let’s create the transitional CrossSection by calling `transition()`

with these two CrossSections as input. If we want the width to vary as a smooth sinusoid between the sections, we can set `width_type`

to `'sine'`

(alternatively we could also use `'linear'`

).

```
[23]:
```

```
# Create the transitional CrossSection
Xtrans = gf.path.transition(cross_section1=X1, cross_section2=X2, width_type="sine")
# Create a Path for the transitional CrossSection to follow
P3 = gf.path.straight(length=15, npoints=100)
# Use the transitional CrossSection to create a Component
straight_transition = gf.path.extrude(P3, Xtrans)
straight_transition
```

```
[23]:
```

```
[24]:
```

```
straight_transition.ports['o1'].cross_section.sections
```

```
[24]:
```

```
[{'width': 1.2,
'offset': 0,
'layer': 2,
'ports': ('o1', 'o2'),
'port_types': ('optical', 'optical'),
'hidden': False,
'name': 'wg'},
{'width': 2.2,
'offset': 0,
'layer': 3,
'ports': (None, None),
'port_types': ('optical', 'optical'),
'hidden': False,
'name': 'etch'},
{'width': 1.1,
'offset': 3,
'layer': 1,
'ports': (None, None),
'port_types': ('optical', 'optical'),
'hidden': False,
'name': 'wg2'}]
```

```
[25]:
```

```
straight_transition.ports['o2'].cross_section.sections
```

```
[25]:
```

```
[{'width': 1,
'offset': 0,
'layer': 2,
'ports': ('o1', 'o2'),
'port_types': ('optical', 'optical'),
'hidden': False,
'name': 'wg'},
{'width': 3.5,
'offset': 0,
'layer': 3,
'ports': (None, None),
'port_types': ('optical', 'optical'),
'hidden': False,
'name': 'etch'},
{'width': 3,
'offset': 5,
'layer': 1,
'ports': (None, None),
'port_types': ('optical', 'optical'),
'hidden': False,
'name': 'wg2'}]
```

```
[26]:
```

```
wg1
```

```
[26]:
```

```
[27]:
```

```
wg2
```

```
[27]:
```

Now that we have all of our components, let’s `connect()`

everything and see what it looks like

```
[28]:
```

```
c = gf.Component()
wg1ref = c << wg1
wgtref = c << straight_transition
wg2ref = c << wg2
wgtref.connect("o1", wg1ref.ports["o2"])
wg2ref.connect("o1", wgtref.ports["o2"])
c
```

```
[28]:
```

Note that since `transition()`

outputs a `CrossSection`

, we can make the transition follow an arbitrary path:

```
[29]:
```

```
# Transition along a curving Path
P4 = gf.path.euler(radius=25, angle=45, p=0.5, use_eff=False)
wg_trans = gf.path.extrude(P4, Xtrans)
c = gf.Component()
wg1_ref = c << wg1 # First cross-section Component
wg2_ref = c << wg2
wgt_ref = c << wg_trans
wgt_ref.connect("o1", wg1_ref.ports["o2"])
wg2_ref.connect("o1", wgt_ref.ports["o2"])
c
```

```
[29]:
```

## Variable width / offset¶

In some instances, you may want to vary the width or offset of the path’s cross- section as it travels. This can be accomplished by giving the `CrossSection`

arguments that are functions or lists. Let’s say we wanted a width that varies sinusoidally along the length of the Path. To do this, we need to make a width function that is parameterized from 0 to 1: for an example function `my_width_fun(t)`

where the width at `t==0`

is the width at the beginning of the Path and the width at
`t==1`

is the width at the end.

```
[30]:
```

```
def my_custom_width_fun(t):
# Note: Custom width/offset functions MUST be vectorizable--you must be able
# to call them with an array input like my_custom_width_fun([0, 0.1, 0.2, 0.3, 0.4])
num_periods = 5
w = 3 + np.cos(2 * np.pi * t * num_periods)
return w
# Create the Path
P = gf.path.straight(length=40)
# Create two cross-sections: one fixed width, one modulated by my_custom_offset_fun
X = gf.CrossSection()
X.add(width=3, offset=-6, layer=0)
X.add(width=my_custom_width_fun, offset=0, layer=0)
# Extrude the Path to create the Component
c = gf.path.extrude(P, cross_section=X)
c
```

```
[30]:
```

We can do the same thing with the offset argument:

```
[31]:
```

```
def my_custom_offset_fun(t):
# Note: Custom width/offset functions MUST be vectorizable--you must be able
# to call them with an array input like my_custom_offset_fun([0, 0.1, 0.2, 0.3, 0.4])
num_periods = 3
w = 3 + np.cos(2 * np.pi * t * num_periods)
return w
# Create the Path
P = gf.path.straight(length=40)
# Create two cross-sections: one fixed offset, one modulated by my_custom_offset_fun
X = gf.CrossSection()
X.add(width=1, offset=my_custom_offset_fun, layer=0)
X.add(width=1, offset=0, layer=0)
# Extrude the Path to create the Component
c = gf.path.extrude(P, cross_section=X)
c
```

```
[31]:
```

## Offsetting a Path¶

Sometimes it’s convenient to start with a simple Path and offset the line it follows to suit your needs (without using a custom-offset CrossSection). Here, we start with two copies of simple straight Path and use the `offset()`

function to directly modify each Path.

```
[32]:
```

```
def my_custom_offset_fun(t):
# Note: Custom width/offset functions MUST be vectorizable--you must be able
# to call them with an array input like my_custom_offset_fun([0, 0.1, 0.2, 0.3, 0.4])
num_periods = 1
w = 2 + np.cos(2 * np.pi * t * num_periods)
return w
P1 = gf.path.straight(length=40)
P2 = P1.copy() # Make a copy of the Path
P1.offset(offset=my_custom_offset_fun)
P2.offset(offset=my_custom_offset_fun)
P2.mirror((1, 0)) # reflect across X-axis
gf.plot([P1, P2])
```

## Modifying a CrossSection¶

In case you need to modify the CrossSection, it can be done simply by specifying a `name`

argument for the cross-sectional element you want to modify later. Here is an example where we name one of thee cross-sectional elements `'myelement1'`

and `'myelement2'`

:

```
[33]:
```

```
# Create the Path
P = gf.path.arc(radius=10, angle=45)
# Create two cross-sections: one fixed width, one modulated by my_custom_offset_fun
X = gf.CrossSection()
X.add(width=1, offset=0, layer=0, ports=("o1", "o2"), name="myelement1")
X.add(width=1, offset=3, layer=0, name="myelement2")
c = gf.path.extrude(P, X)
c
```

```
[33]:
```

In case we want to change any of the CrossSection elements, we simply access the Python dictionary that specifies that element and modify the values

```
[34]:
```

```
# Copy our original CrossSection
Xcopy = X.copy()
# Modify
Xcopy["myelement2"]["width"] = 2 # X['myelement2'] is a dictionary
Xcopy["myelement2"]["layer"] = 1 # X['myelement2'] is a dictionary
# Extrude the Path to create the Device
c = gf.path.extrude(P, cross_section=Xcopy)
c
```

```
[34]:
```

```
[35]:
```

```
import gdsfactory as gf
X1 = gf.CrossSection()
X1.add(width=1.2, offset=0, layer=2, name="wg", ports=("o1", "o2"))
X1.add(width=2.2, offset=0, layer=3, name="etch")
X1.add(width=1.1, offset=3, layer=1, name="wg2")
# Create the second CrossSection that we want to transition to
X2 = gf.CrossSection()
X2.add(width=1, offset=0, layer=2, name="wg", ports=("o1", "o2"))
X2.add(width=3.5, offset=0, layer=3, name="etch")
X2.add(width=3, offset=5, layer=1, name="wg2")
Xtrans = gf.path.transition(cross_section1=X1, cross_section2=X2, width_type="sine")
P1 = gf.path.straight(length=5)
P2 = gf.path.straight(length=5)
wg1 = gf.path.extrude(P1, X1)
wg2 = gf.path.extrude(P2, X2)
P4 = gf.path.euler(radius=25, angle=45, p=0.5, use_eff=False)
wg_trans = gf.path.extrude(P4, Xtrans)
# WG_trans = P4.extrude(Xtrans)
c = gf.Component()
wg1_ref = c << wg1
wg2_ref = c << wg2
wgt_ref = c << wg_trans
wgt_ref.connect("o1", wg1_ref.ports["o2"])
wg2_ref.connect("o1", wgt_ref.ports["o2"])
c
```

```
[35]:
```

```
[36]:
```

```
len(c.references)
```

```
[36]:
```

```
3
```

**Note**

Any unamed section in the CrossSection won’t be transitioned.

If you don’t add any named sections in a cross-section it will give you an error when making a transition

```
[37]:
```

```
import gdsfactory as gf
import numpy as np
P = gf.Path()
P.append(gf.path.arc(radius=10, angle=90)) # Circular arc
P.append(gf.path.straight(length=10)) # Straight section
P.append(gf.path.euler(radius=3, angle=-90)) # Euler bend (aka "racetrack" curve)
P.append(gf.path.straight(length=40))
P.append(gf.path.arc(radius=8, angle=-45))
P.append(gf.path.straight(length=10))
P.append(gf.path.arc(radius=8, angle=45))
P.append(gf.path.straight(length=10))
gf.plot(P)
```

```
[38]:
```

```
X = gf.CrossSection()
X.add(width=1, offset=0, layer=0)
c = gf.path.extrude(P, X)
c
```

```
[38]:
```

```
[39]:
```

```
X2 = gf.CrossSection()
X2.add(width=2, offset=0, layer=0)
c = gf.path.extrude(P, X2)
c
```

```
[39]:
```

For example this will give you an error

```
T = gf.path.transition(X, X2)
```

**Solution**

```
[40]:
```

```
P = gf.path.straight(length=10, npoints=101)
X = gf.CrossSection()
X.add(width=1, offset=0, layer=gf.LAYER.WG, name="core", ports=("o1", "o2"))
X.add(width=3, offset=0, layer=gf.LAYER.SLAB90)
c = gf.path.extrude(P, X)
c
```

```
[40]:
```

```
[41]:
```

```
X2 = gf.CrossSection()
X2.add(width=3, offset=0, layer=gf.LAYER.WG, name="core", ports=("o1", "o2"))
c2 = gf.path.extrude(P, X2)
c2
```

```
[41]:
```

```
[42]:
```

```
T = gf.path.transition(X, X2)
c3 = gf.path.extrude(P, T)
c3
```

```
[42]:
```

```
[43]:
```

```
c4 = gf.Component()
```

```
[44]:
```

```
start_ref = c4 << c
trans_ref = c4 << c3
end_ref = c4 << c2
trans_ref.connect("o1", start_ref.ports["o2"])
end_ref.connect("o1", trans_ref.ports["o2"])
```

```
[44]:
```

```
DeviceReference (parent Device "path_0f3b461c5438bbd4f4a9c17b53", ports ['o1', 'o2'], origin [20. 0.], rotation 0, x_reflection False)
```

```
[45]:
```

```
c4
```

```
[45]:
```

## cross-section¶

You can create functions that return a cross_section in 2 ways:

`gf.partial`

can customize an existing cross-section for example`gf.cross_section.strip`

define a function that returns a cross_section

```
[46]:
```

```
import gdsfactory as gf
from gdsfactory.tech import Section
```

```
[47]:
```

```
pin = gf.partial(
gf.cross_section.strip,
layer=(2, 0),
sections=(
Section(layer=gf.LAYER.P, width=2, offset=+2),
Section(layer=gf.LAYER.N, width=2, offset=-2),
),
)
```

```
[48]:
```

```
c = gf.components.straight(cross_section=pin)
c
```

```
[48]:
```

```
[49]:
```

```
gf.components.straight(cross_section=pin)
```

```
[49]:
```

finally, you can also pass most components Dict that define the cross-section

```
[50]:
```

```
gf.components.straight(
layer=(1, 0),
width=0.5,
sections=(
Section(layer=gf.LAYER.P, width=1, offset=+2),
Section(layer=gf.LAYER.N, width=1, offset=-2),
),
)
```

```
[50]:
```

```
[51]:
```

```
import numpy as np
import gdsfactory as gf
# Create our first CrossSection
X1 = gf.CrossSection()
X1.add(width=0.5, offset=0, layer=1, name="wg", ports=("o1", "o2"))
X1.add(width=0.2, offset=0, layer=3, name="slab")
# Create the second CrossSection that we want to transition to
X2 = gf.CrossSection()
X2.add(width=0.5, offset=0, layer=1, name="wg", ports=("o1", "o2"))
X2.add(width=3.0, offset=0, layer=3, name="slab")
# To show the cross-sections, let's create two Paths and
# create Devices by extruding them
P1 = gf.path.straight(length=5)
P2 = gf.path.straight(length=5)
wg1 = gf.path.extrude(P1, X1)
wg2 = gf.path.extrude(P2, X2)
# Place both cross-section Devices and quickplot them
c = gf.Component()
wg1ref = c << wg1
wg2ref = c << wg2
wg2ref.movex(7.5)
# Create the transitional CrossSection
Xtrans = gf.path.transition(cross_section1=X1, cross_section2=X2, width_type="linear")
# Create a Path for the transitional CrossSection to follow
P3 = gf.path.straight(length=15, npoints=100)
# Use the transitional CrossSection to create a Component
straight_transition = gf.path.extrude(P3, Xtrans)
straight_transition
```

```
[51]:
```

```
[ ]:
```

```
```