Source code for lnoi400.spline

import gdsfactory as gf
import matplotlib.pyplot as plt
import numpy as np
from gdsfactory.typings import Coordinate, CrossSectionSpec


def spline_clamped_path(
    t: np.ndarray, start: Coordinate = (0.0, 0.0), end: Coordinate = (120.0, 25.0)
):
    """Returns a spline path with a null first derivative at the extrema."""

    xs = t
    ys = (t**2) * (3 - 2 * t)

    # Rescale to the start and end coordinates

    xs = start[0] + (end[0] - start[0]) * xs
    ys = start[1] + (end[1] - start[1]) * ys

    path = gf.Path(np.column_stack([xs, ys]))
    path.start_angle = path.end_angle = 0.0

    return path


def spline_null_curvature(
    t: np.ndarray, start: Coordinate = (0.0, 0.0), end: Coordinate = (120.0, 25.0)
):
    """Returns a spline path with zero first and second derivatives at the extrema."""

    xs = t
    ys = (t**3) * (6 * t**2 - 15.0 * t + 10.0)

    xs = start[0] + (end[0] - start[0]) * xs
    ys = start[1] + (end[1] - start[1]) * ys

    path = gf.Path(np.column_stack([xs, ys]))
    path.start_angle = path.end_angle = 0.0

    return path


[docs] @gf.cell def bend_S_spline( size: tuple[float, float] = (100.0, 30.0), cross_section: CrossSectionSpec = "xs_rwg1000", npoints: int = 201, path_method=spline_clamped_path, ) -> gf.Component: """A spline bend merging a vertical offset.""" t = np.linspace(0, 1, npoints) xs = gf.get_cross_section(cross_section) path = path_method(t, start=(0.0, 0.0), end=size) c = path.extrude(xs) return c
[docs] @gf.cell def bend_S_spline_varying_width( size: tuple[float, float] = (58, 14.5), cross_section1: CrossSectionSpec = None, cross_section2: CrossSectionSpec = None, npoints: int = 201, path_method=spline_null_curvature, ) -> gf.Component: """ A spline bend merging a vertical offset. Can accept arbitrary cross sections. Not tested as a standalone PDK element. Used as a building block for cells with known behaviour. """ if not cross_section1: s0 = gf.Section( width=0.2, offset=0, layer="LN_RIDGE", name="_default", port_names=("o1", "o2"), ) s1 = gf.Section( width=10.0, offset=0, layer="LN_SLAB", name="slab", simplify=0.03 ) cross_section1 = gf.CrossSection(sections=[s0, s1]) if not cross_section2: s0 = gf.Section( width=0.3, offset=0, layer="LN_RIDGE", name="_default", port_names=("o1", "o2"), ) s1 = gf.Section( width=10.0, offset=0, layer="LN_SLAB", name="slab", simplify=0.03 ) cross_section2 = gf.CrossSection(sections=[s0, s1]) t = np.linspace(0, 1, npoints) path = path_method(t, start=(0.0, 0.0), end=size) xtrans = gf.path.transition( cross_section1=cross_section1, cross_section2=cross_section2, width_type="linear", ) return gf.path.extrude_transition(path, xtrans)
if __name__ == "__main__": # Visualize differences between spline and bezier path t = np.linspace(0, 1, 600) apath = spline_null_curvature(t, end=(50.0, 15.0)) bpath = spline_clamped_path(t, end=(50.0, 15.0)) _, ka = apath.curvature() _, kb = bpath.curvature() plot_args_a = { "linewidth": 2.1, "label": "Zero curvature", } plot_args_b = { "linewidth": plot_args_a["linewidth"], "label": "Zero derivative", } ap = apath.points bp = bpath.points # ka = np.column_stack((ap[:-1, 0], curv_apath)) # kb = np.column_stack((bp[:-1, 0], curv_bpath)) fig, axs = plt.subplots(1, 2, figsize=(9, 3.5), tight_layout=True) axs[0].plot(ap[:, 0], ap[:, 1], **plot_args_a) axs[0].plot(bp[:, 0], bp[:, 1], **plot_args_b) axs[0].set_xlabel("x (um)") axs[0].set_ylabel("y (um)") axs[1].plot(t[0:-1], ka, **plot_args_a) axs[1].plot(t[0:-1], kb, **plot_args_b) axs[1].set_xlabel("x (um)") axs[1].set_ylabel("Curvature (arb.)") [axs[k].legend(loc="best") for k in range(2)] plt.show()