Custom rasterization style#

This examples shows, how a custom rasterization style can be created.

In this example, a style for 1-dim is implemented where the dwell times of the rasterized spots of the shape are not equal but linearly ramped between ramp_start and ramp_end. E.g. if ramp_start = 1 and ramp_end = 5 and the number of rasterized spots are five, the dwell times would be [1, 2, 3, 4, 5] * mill.dwell_time.

To get started, all necessary fib-o-mat modules are imported and a subclass of RasterStyle is created with a corresponding __init__ method

import numpy as np

from fibomat.raster_styles import RasterStyle
from fibomat.units import LengthQuantity, has_length_dim, LengthUnit, TimeUnit, scale_to, scale_factor
from fibomat.dimensioned_object import DimObjLike, DimObj
from fibomat.shapes import Shape
from fibomat.mill import Mill
from fibomat.rasterizedpattern import RasterizedPattern
from fibomat.curve_tools import rasterize


class ConsecutiveRamped(RasterStyle):
    def __init__(self, pitch: LengthQuantity, ramp_start: float, ramp_end: float):
        """
        Raster style with ramped dwell times.
        The first spot will have dwell time ``mill.dwell_time * ramp_start`` and the last
        ``mill.dwell_time * ramp_end``. All others are linearly interpolated.


        Args:
            pitch (LengthQuantity): pitch of spots
            ramp_start (float): ramp start
            ramp_end (float): ramp end
        """
        if not has_length_dim(pitch):
            raise ValueError('pitch must have dimension [length].')
        self._pitch = pitch

        if ramp_start < 0 or ramp_end < 0:
            raise ValueError('ramp_start and ramp_end must be greater than 0.')

        self._ramp_start = ramp_start
        self._ramp_end = ramp_end

Next, the base method dimension() is implemented. This method returns an integer indicating the dimensionality of the shapes which can be rasterized by this class.

def dimension(self) -> int:
    return 1

Here, the dimensionality is constant one.

TODO: check if shape has right dimension!

Finally, the actual rasterization method (rasterize()) must be provided. See the documented code below for details.

def rasterize(
    self,
    dim_shape: DimObjLike[Shape, LengthUnit],
    mill: Mill,
    out_length_unit: LengthUnit,
    out_time_unit: TimeUnit
) -> RasterizedPattern:
    # Check if we actually got a dimensioned object. If not, this call would raise an exception.
    dim_shape = DimObj.create(dim_shape)

    # Rasterize the passed shape with the pitch provided by the user.
    # The pitch must be scaled to the shape unit first to be consistent.
    points = np.array(rasterize(dim_shape.obj, scale_to(dim_shape.unit, self._pitch)).dwell_points)

    # Assign the dwell ramp to the weights of points
    points[:, 2] = np.linspace(self._ramp_start, self._ramp_end, len(points))

    # Scale the mill.dwell_time to the output time unit and multiply it to the dwell ramp values.
    points[:, 2] *= scale_to(out_time_unit, mill.dwell_time)

    # Scale the spots to the output length unit
    points[:, :2] *= scale_factor(out_length_unit, dim_shape.unit)

    # Create a RasterizedPattern object and return it.
    # np.tile repeats the points mill.repeats times.
    return RasterizedPattern(
        np.tile(points, (mill.repeats, 1)),
        length_unit=out_length_unit,
        time_unit=out_time_unit
    )

Now, the rasterizing backend can be used

from fibomat import Sample, U_, Q_, Mill
from fibomat import shapes, default_backends

s = Sample()
site = s.create_site(
    dim_position=((0 ,0), U_('µm')),
    dim_fov=((1, 1), U_('µm'))
)
site.create_pattern(
    dim_shape=(shapes.Line((-.75, -.75), (.75, .75)), U_('µm')),
    mill=Mill(dwell_time=Q_('1 ms'), repeats=5),
    raster_style=ConsecutiveRamped(pitch=Q_('1 nm'), ramp_start=1, ramp_end=np.pi)
)
s.export(default_backends.SpotListBackend).save('rasterized.txt')

The complete source code can be found at viggge/fib-o-mat/-/blob/master/examples/custom_rasterization_style.py.