Ejemplo n.º 1
0
def SplitDisconectedParts(polydata):
    """
    """
    conn = vtkPolyDataConnectivityFilter()
    conn.SetInputData(polydata)
    conn.SetExtractionModeToAllRegions()
    conn.Update()

    nregions = conn.GetNumberOfExtractedRegions()

    conn.SetExtractionModeToSpecifiedRegions()
    conn.Update()

    polydata_collection = []

    # Update progress value in GUI
    progress = nregions -1
    if progress:
        UpdateProgress = vu.ShowProgress(progress)

    for region in range(nregions):
        conn.InitializeSpecifiedRegionList()
        conn.AddSpecifiedRegion(region)
        conn.Update()

        p = vtkPolyData()
        p.DeepCopy(conn.GetOutput())

        polydata_collection.append(p)
        if progress:
            UpdateProgress(region, _("Splitting disconnected regions..."))

    return polydata_collection
Ejemplo n.º 2
0
def SelectLargestPart(polydata):
    """
    """
    UpdateProgress = vu.ShowProgress(1)
    conn = vtkPolyDataConnectivityFilter()
    conn.SetInputData(polydata)
    conn.SetExtractionModeToLargestRegion()
    conn.AddObserver("ProgressEvent", lambda obj, evt:
                  UpdateProgress(conn, "Getting largest part..."))
    conn.Update()

    result = vtkPolyData()
    result.DeepCopy(conn.GetOutput())
    return result
Ejemplo n.º 3
0
    def PeelDown(self, currentPeel):
        for i in range(0, self.numberOfPeels):
            currentPeel = self.SliceDown(currentPeel)
            currentPeel = self.MapImageOnCurrentPeel(currentPeel)

            newPeel = vtkPolyData()
            newPeel.DeepCopy(currentPeel)
            self.peel.append(newPeel)

            # GetCurrentPeelActor()
            # newPeelActor = vtkActor()
            # newPeelActor = currentPeelActor
            # peelActors.push_back(newPeelActor)

            self.currentPeelNo += 1
Ejemplo n.º 4
0
    def SliceDown(self, currentPeel):
        # Warp using the normals
        warp = vtkWarpVector()
        warp.SetInputData(fixMesh(downsample(currentPeel)))  # fixMesh here updates normals needed for warping
        warp.SetInputArrayToProcess(0, 0, 0, vtkDataObject().FIELD_ASSOCIATION_POINTS,
                                    vtkDataSetAttributes().NORMALS)
        warp.SetScaleFactor(-1)
        warp.Update()

        out = vtkPolyData()
        out = upsample(warp.GetPolyDataOutput())
        out = smooth(out)
        out = fixMesh(out)
        out = cleanMesh(out)

        currentPeel = out
        return currentPeel
Ejemplo n.º 5
0
def writepolydata(filename, points, normals, cells):
    polydata = vtkPolyData()
    vtkpoints = vtkPoints()
    vtkpoints.SetData(numpy_to_vtk(points))
    polydata.SetPoints(vtkpoints)
    polydata.GetPointData().SetNormals(numpy_to_vtk(normals))
    triangles = vtkCellArray()
    for i, j, k in cells:
        triangles.InsertNextCell(3)
        triangles.InsertCellPoint(i)
        triangles.InsertCellPoint(j)
        triangles.InsertCellPoint(k)
    polydata.SetPolys(triangles)
    wr = vtkPolyDataWriter()
    wr.SetFileName(filename)
    wr.SetInputData(polydata)
    wr.Write()
    def write_poly_data(cls, var, filename: str, attr_names: str) -> None:
        vol = vtkPolyData()
        verts = vtkPoints()
        # noinspection PyArgumentList
        lines = vtkCellArray()

        # Retained, but not used. 'Fungus' is current, not 'Afumigatus'
        # if isinstance(var, AfumigatusCellTreeList):
        #     adjacency = var.adjacency
        #
        #     for i, j in adjacency.keys():
        #         if i != j:
        #             line = vtkLine()
        #             line.GetPointIds().SetId(0, i)
        #             line.GetPointIds().SetId(1, j)
        #             lines.InsertNextCell(line)
        #
        # el
        if not isinstance(var, CellList):
            raise NotImplementedError(
                f'Only supported CellTree or CellList for POLY_DATA. \
                Got {type(var)}'
            )

        for index in var.alive():
            cell = var[index]
            # noinspection PyArgumentList
            verts.InsertNextPoint(cell['point'][2], cell['point'][1], cell['point'][0])

        alive_cells = np.take(var.cell_data, var.alive())
        for attr_name in attr_names:
            cell_attr = alive_cells[attr_name]
            scalars = numpy_to_vtk(num_array=cell_attr)
            scalars.SetName(attr_name)
            vol.GetPointData().AddArray(scalars)

        vol.SetPoints(verts)
        vol.SetLines(lines)
        writer = vtkPolyDataWriter()
        writer.SetFileName(filename)
        writer.SetInputData(vol)
        writer.Write()
Ejemplo n.º 7
0
def JoinSeedsParts(polydata, point_id_list):
    """
    The function require vtkPolyData and point id
    from vtkPolyData.
    """
    conn = vtkPolyDataConnectivityFilter()
    conn.SetInputData(polydata)
    conn.SetExtractionModeToPointSeededRegions()
    UpdateProgress = vu.ShowProgress(1 + len(point_id_list))
    pos = 1
    for seed in point_id_list:
        conn.AddSeed(seed)
        UpdateProgress(pos, _("Analysing selected regions..."))
        pos += 1

    conn.AddObserver("ProgressEvent", lambda obj, evt:
                  UpdateProgress(conn, "Getting selected parts"))
    conn.Update()

    result = vtkPolyData()
    result.DeepCopy(conn.GetOutput())
    return result
Ejemplo n.º 8
0
def compute_tubes(trk, direction):
    """Compute and assign colors to a vtkTube for visualization of a single tract

    :param trk: nx3 array of doubles (x, y, z) point coordinates composing the tract
    :type trk: numpy.ndarray
    :param direction: nx3 array of int (x, y, z) RGB colors in the range 0 - 255
    :type direction: numpy.ndarray
    :return: a vtkTubeFilter instance
    :rtype: vtkTubeFilter
    """

    numb_points = trk.shape[0]
    points = vtkPoints()
    lines = vtkCellArray()

    colors = vtkUnsignedCharArray()
    colors.SetNumberOfComponents(4)

    k = 0
    lines.InsertNextCell(numb_points)
    for j in range(numb_points):
        points.InsertNextPoint(trk[j, :])
        colors.InsertNextTuple(direction[j, :])
        lines.InsertCellPoint(k)
        k += 1

    trk_data = vtkPolyData()
    trk_data.SetPoints(points)
    trk_data.SetLines(lines)
    trk_data.GetPointData().SetScalars(colors)

    # make it a tube
    trk_tube = vtkTubeFilter()
    trk_tube.SetRadius(0.5)
    trk_tube.SetNumberOfSides(4)
    trk_tube.SetInputData(trk_data)
    trk_tube.Update()

    return trk_tube
Ejemplo n.º 9
0
def convert_cells_to_vtk(cells: CellList) -> vtkPolyData:
    cell_data: CellData = cells.cell_data
    live_cells = cells.alive()
    cell_data = cell_data[live_cells]

    fields = dict(cell_data.dtype.fields)  # type: ignore
    fields.pop('point')

    points = vtkPoints()
    poly = vtkPolyData()
    poly.SetPoints(points)

    if not len(cell_data):
        return poly

    # vtk uses coordinate ordering x, y, z while we use z, y, x.
    points.SetData(numpy_to_vtk(np.flip(cell_data['point'], axis=1)))
    point_data = poly.GetPointData()

    for field, (dtype, *_) in fields.items():
        data = cell_data[field]

        # numpy_to_vtk doesn't handle bool for some reason
        if dtype == np.dtype('bool'):
            data = data.astype(np.dtype('uint8'))

        # noinspection PyBroadException
        try:
            scalar = numpy_to_vtk(data)
        except Exception:
            print(f'Unhandled data type in field {field}')
            continue

        scalar.SetName(field)
        point_data.AddArray(scalar)

    return poly
Ejemplo n.º 10
0
    def RequestData(self, request, inInfoVec, outInfoVec):
        from vtkmodules.vtkCommonCore import vtkFloatArray, vtkPoints
        from vtkmodules.vtkCommonDataModel import (
            vtkCellArray, vtkCompositeDataSet, vtkPartitionedDataSet,
            vtkPartitionedDataSetCollection, vtkPolyData)

        output = vtkPartitionedDataSetCollection.GetData(outInfoVec)
        partitioned_datasets = []
        partitioned_dataset_names = []

        # Parse line file
        if not self._filename:
            print_error("SAVGReader requires a FileName")
            return 0

        # Stores lines of text from the file associated with each group of
        # geometries encountered.
        geometries = {"lines": [], "points": [], "poly": []}

        # Read the file and build up data structure to hold the primitives
        with open(self._filename, "r") as file:
            current = None
            for line in file:
                parts = line.split("#")
                line = parts[0].strip().lower()

                if len(line) < 1:
                    continue

                if not_supported(line):
                    continue

                if line.startswith("lin"):
                    geometries["lines"].append({"rgba": None, "values": []})
                    current = geometries["lines"][-1]
                    line_parts = line.split(" ")
                    if len(line_parts) == 5:
                        current["rgba"] = [float(n) for n in line_parts[1:]]
                elif line.startswith("point"):
                    geometries["points"].append({
                        "rgba": None,
                        "values": [],
                    })
                    current = geometries["points"][-1]
                    line_parts = line.split(" ")
                    if len(line_parts) == 5:
                        current["rgba"] = [float(n) for n in line_parts[1:]]
                elif line.startswith("poly"):
                    geometries["poly"].append({
                        "rgba": None,
                        "npts": None,
                        "values": [],
                    })
                    current = geometries["poly"][-1]
                    line_parts = line.split(" ")
                    if len(line_parts) == 2:
                        current["npts"] = int(line_parts[1])
                    elif len(line_parts) == 6:
                        current["rgba"] = [float(n) for n in line_parts[1:5]]
                        current["npts"] = int(line_parts[5])
                elif line.startswith("end"):
                    current = None
                else:
                    if current is not None:
                        if "npts" in current and current["npts"] is not None:
                            # polygon, known num pts per poly
                            if len(current["values"]) == current["npts"]:
                                # Reached the number of points for the current one,
                                # start a new one.
                                geometries["poly"].append({
                                    "npts":
                                    current["npts"],
                                    "rgba":
                                    current["rgba"],
                                    "values": []
                                })
                                current = geometries["poly"][-1]
                        pt, pt_col, pt_n = get_coords_from_line(line)
                        if pt:
                            current["values"].append({
                                "pos": pt,
                            })
                            color = pt_col or current["rgba"]
                            if color:
                                current["values"][-1]["col"] = color
                            if pt_n:
                                current["values"][-1]["norm"] = pt_n

        # Build lines polydata if there were any lines
        if geometries["lines"]:
            line_points = vtkPoints()
            line_cells = vtkCellArray()
            line_point_colors = vtkFloatArray()
            line_point_colors.SetNumberOfComponents(4)
            line_point_colors.SetName("rgba_colors")
            line_point_normals = vtkFloatArray()
            line_point_normals.SetNumberOfComponents(3)
            line_point_normals.SetName("vertex_normals")

            pt_count = 0

            for batch in geometries["lines"]:
                num_in_batch = len(batch["values"])
                first_in_batch = True
                for coord in batch["values"]:
                    if "pos" in coord:
                        line_points.InsertNextPoint(coord["pos"])
                    if "norm" in coord:
                        line_point_normals.InsertNextTuple(coord["norm"])
                    if "col" in coord:
                        line_point_colors.InsertNextTuple(coord["col"])

                    if first_in_batch:
                        line_cells.InsertNextCell(num_in_batch)
                        first_in_batch = False

                    line_cells.InsertCellPoint(pt_count)

                    pt_count += 1

            output_lines = vtkPolyData()

            output_lines.SetPoints(line_points)
            output_lines.SetLines(line_cells)

            if line_point_colors.GetNumberOfTuples() > 0:
                output_lines.GetPointData().AddArray(line_point_colors)

            if line_point_normals.GetNumberOfTuples() > 0:
                output_lines.GetPointData().AddArray(line_point_normals)

            ds = vtkPartitionedDataSet()
            ds.SetNumberOfPartitions(1)
            ds.SetPartition(0, output_lines)

            partitioned_datasets.append(ds)
            partitioned_dataset_names.append("Lines")

        # Build the points polydata if we found points
        if geometries["points"]:
            p_points = vtkPoints()
            p_cells = vtkCellArray()
            p_point_colors = vtkFloatArray()
            p_point_colors.SetNumberOfComponents(4)
            p_point_colors.SetName("rgba_colors")
            p_point_normals = vtkFloatArray()
            p_point_normals.SetNumberOfComponents(3)
            p_point_normals.SetName("vertex_normals")

            p_count = 0

            for batch in geometries["points"]:
                num_in_batch = len(batch["values"])
                first_in_batch = True
                for coord in batch["values"]:
                    if "pos" in coord:
                        p_points.InsertNextPoint(coord["pos"])
                    if "norm" in coord:
                        p_point_normals.InsertNextTuple(coord["norm"])
                    if "col" in coord:
                        p_point_colors.InsertNextTuple(coord["col"])

                    if first_in_batch:
                        p_cells.InsertNextCell(num_in_batch)
                        first_in_batch = False

                    p_cells.InsertCellPoint(p_count)

                    p_count += 1

            output_points = vtkPolyData()

            output_points.SetPoints(p_points)
            output_points.SetVerts(p_cells)

            if p_point_colors.GetNumberOfTuples() > 0:
                output_points.GetPointData().AddArray(p_point_colors)

            if p_point_normals.GetNumberOfTuples() > 0:
                output_points.GetPointData().AddArray(p_point_normals)

            ds = vtkPartitionedDataSet()
            ds.SetNumberOfPartitions(1)
            ds.SetPartition(0, output_points)

            partitioned_datasets.append(ds)
            partitioned_dataset_names.append("Points")

        # Build the polygons if there were any
        if geometries["poly"]:
            poly_points = vtkPoints()
            poly_cells = vtkCellArray()
            poly_point_colors = vtkFloatArray()
            poly_point_colors.SetNumberOfComponents(4)
            poly_point_colors.SetName("rgba_colors")
            poly_point_normals = vtkFloatArray()
            poly_point_normals.SetNumberOfComponents(3)
            poly_point_normals.SetName("vertex_normals")

            pt_count = 0

            for batch in geometries["poly"]:
                num_in_batch = len(batch["values"])
                if num_in_batch < 1:
                    continue
                first_in_batch = True
                for coord in batch["values"]:
                    if "pos" in coord:
                        poly_points.InsertNextPoint(coord["pos"])
                    if "norm" in coord:
                        poly_point_normals.InsertNextTuple(coord["norm"])
                    if "col" in coord:
                        poly_point_colors.InsertNextTuple(coord["col"])

                    if first_in_batch:
                        np_in_cell = num_in_batch
                        poly_cells.InsertNextCell(np_in_cell)
                        first_in_batch = False

                    poly_cells.InsertCellPoint(pt_count)

                    pt_count += 1

            output_polys = vtkPolyData()

            output_polys.SetPoints(poly_points)
            output_polys.SetPolys(poly_cells)

            if poly_point_colors.GetNumberOfTuples() > 0:
                output_polys.GetPointData().AddArray(poly_point_colors)

            if poly_point_normals.GetNumberOfTuples() > 0:
                output_polys.GetPointData().AddArray(poly_point_normals)

            ds = vtkPartitionedDataSet()
            ds.SetNumberOfPartitions(1)
            ds.SetPartition(0, output_polys)

            partitioned_datasets.append(ds)
            partitioned_dataset_names.append("Polygons")

        # Add any partioned datasets we created
        output.SetNumberOfPartitionedDataSets(len(partitioned_datasets))

        for idx, pds in enumerate(partitioned_datasets):
            output.SetPartitionedDataSet(idx, pds)
            output.GetMetaData(idx).Set(vtkCompositeDataSet.NAME(),
                                        partitioned_dataset_names[idx])

        return 1
Ejemplo n.º 11
0
    def _do_surface_creation(self, mask, mask_sFormMatrix=None):
        if mask_sFormMatrix is None:
            mask_sFormMatrix = vtkMatrix4x4()

        value = np.mean(mask.GetScalarRange())

        # Use the mask to create isosurface
        mc = vtkContourFilter()
        mc.SetInputData(mask)
        mc.SetValue(0, value)
        mc.ComputeNormalsOn()
        mc.Update()

        # Mask isosurface
        refSurface = mc.GetOutput()

        # Create a uniformly meshed surface
        tmpPeel = downsample(refSurface)
        # Standard space coordinates

        # Apply coordinate transform to the meshed mask
        mask_ijk2xyz = vtkTransform()
        mask_ijk2xyz.SetMatrix(mask_sFormMatrix)

        mask_ijk2xyz_filter = vtkTransformPolyDataFilter()
        mask_ijk2xyz_filter.SetInputData(tmpPeel)
        mask_ijk2xyz_filter.SetTransform(mask_ijk2xyz)
        mask_ijk2xyz_filter.Update()

        # Smooth the mesh
        tmpPeel = smooth(mask_ijk2xyz_filter.GetOutput())
        # Configure calculation of normals
        tmpPeel = fixMesh(tmpPeel)
        # Remove duplicate points etc
        # tmpPeel = cleanMesh(tmpPeel)
        # Generate triangles
        tmpPeel = upsample(tmpPeel)

        tmpPeel = smooth(tmpPeel)
        tmpPeel = fixMesh(tmpPeel)
        tmpPeel = cleanMesh(tmpPeel)

        refImageSpace2_xyz_transform = vtkTransform()
        refImageSpace2_xyz_transform.SetMatrix(vtk_utils.numpy_to_vtkMatrix4x4(np.linalg.inv(self.affine)))

        self.refImageSpace2_xyz = vtkTransformPolyDataFilter()
        self.refImageSpace2_xyz.SetTransform(refImageSpace2_xyz_transform)

        xyz2_refImageSpace_transform = vtkTransform()
        xyz2_refImageSpace_transform.SetMatrix(vtk_utils.numpy_to_vtkMatrix4x4(self.affine))

        self.xyz2_refImageSpace = vtkTransformPolyDataFilter()
        self.xyz2_refImageSpace.SetTransform(xyz2_refImageSpace_transform)

        currentPeel = tmpPeel
        self.currentPeelNo = 0
        currentPeel= self.MapImageOnCurrentPeel(currentPeel)

        newPeel = vtkPolyData()
        newPeel.DeepCopy(currentPeel)
        newPeel.DeepCopy(currentPeel)
        self.peel_normals = vtkFloatArray()
        self.peel_centers = vtkFloatArray()
        self.peel.append(newPeel)
        self.currentPeelActor = vtkActor()
        if not np.all(np.equal(self.affine, np.eye(4))):
            affine_vtk = self.CreateTransformedVTKAffine()
            self.currentPeelActor.SetUserMatrix(affine_vtk)
        self.GetCurrentPeelActor(currentPeel)
        self.peelActors.append(self.currentPeelActor)
        # locator will later find the triangle on the peel surface where the coil's normal intersect
        self.locator = vtkCellLocator()
        self.PeelDown(currentPeel)
Ejemplo n.º 12
0
def ribbon(molecule):
    """Create an actor for ribbon molecular representation.

    Parameters
    ----------
    molecule : Molecule
        The molecule to be rendered.

    Returns
    -------
    molecule_actor : vtkActor
        Actor created to render the rubbon representation of the molecule to be
        visualized.

    References
    ----------
    Richardson, J.S. The anatomy and taxonomy of protein structure
    `Advances in Protein Chemistry, 1981, 34, 167-339.
    <https://doi.org/10.1016/S0065-3233(08)60520-3>`_
    """
    coords = get_all_atomic_positions(molecule)
    all_atomic_numbers = get_all_atomic_numbers(molecule)
    num_total_atoms = molecule.total_num_atoms
    secondary_structures = np.ones(num_total_atoms)
    for i in range(num_total_atoms):
        secondary_structures[i] = ord('c')
        resi = molecule.residue_seq[i]
        for j, _ in enumerate(molecule.sheet):
            sheet = molecule.sheet[j]
            if molecule.chain[i] != sheet[0] or resi < sheet[1] or \
               resi > sheet[3]:
                continue
            secondary_structures[i] = ord('s')

        for j, _ in enumerate(molecule.helix):
            helix = molecule.helix[j]
            if molecule.chain[i] != helix[0] or resi < helix[1] or \
               resi > helix[3]:
                continue
            secondary_structures[i] = ord('h')

    output = cdmvtk.vtkPolyData()

    # for atomic numbers
    atomic_num_arr = numpy_to_vtk(num_array=all_atomic_numbers,
                                  deep=True,
                                  array_type=ccvtk.VTK_ID_TYPE)

    # setting the array name to atom_type as vtkProteinRibbonFilter requires
    # the array to be named atom_type
    atomic_num_arr.SetName("atom_type")

    output.GetPointData().AddArray(atomic_num_arr)

    # for atom names
    atom_names = ccvtk.vtkStringArray()

    # setting the array name to atom_types as vtkProteinRibbonFilter requires
    # the array to be named atom_types
    atom_names.SetName("atom_types")
    atom_names.SetNumberOfTuples(num_total_atoms)
    for i in range(num_total_atoms):
        atom_names.SetValue(i, molecule.atom_names[i])

    output.GetPointData().AddArray(atom_names)

    # for residue sequences
    residue_seq = numpy_to_vtk(num_array=molecule.residue_seq,
                               deep=True,
                               array_type=ccvtk.VTK_ID_TYPE)
    residue_seq.SetName("residue")
    output.GetPointData().AddArray(residue_seq)

    # for chain
    chain = numpy_to_vtk(num_array=molecule.chain,
                         deep=True,
                         array_type=ccvtk.VTK_UNSIGNED_CHAR)
    chain.SetName("chain")
    output.GetPointData().AddArray(chain)

    # for secondary structures
    s_s = numpy_to_vtk(num_array=secondary_structures,
                       deep=True,
                       array_type=ccvtk.VTK_UNSIGNED_CHAR)
    s_s.SetName("secondary_structures")
    output.GetPointData().AddArray(s_s)

    # for secondary structures begin
    newarr = np.ones(num_total_atoms)
    s_sb = numpy_to_vtk(num_array=newarr,
                        deep=True,
                        array_type=ccvtk.VTK_UNSIGNED_CHAR)
    s_sb.SetName("secondary_structures_begin")
    output.GetPointData().AddArray(s_sb)

    # for secondary structures end
    newarr = np.ones(num_total_atoms)
    s_se = numpy_to_vtk(num_array=newarr,
                        deep=True,
                        array_type=ccvtk.VTK_UNSIGNED_CHAR)
    s_se.SetName("secondary_structures_end")
    output.GetPointData().AddArray(s_se)

    # for is_hetatm
    is_hetatm = numpy_to_vtk(num_array=molecule.is_hetatm,
                             deep=True,
                             array_type=ccvtk.VTK_UNSIGNED_CHAR)
    is_hetatm.SetName("ishetatm")
    output.GetPointData().AddArray(is_hetatm)

    # for model
    model = numpy_to_vtk(num_array=molecule.model,
                         deep=True,
                         array_type=ccvtk.VTK_UNSIGNED_INT)
    model.SetName("model")
    output.GetPointData().AddArray(model)

    table = PeriodicTable()

    # for colors and radii of hetero-atoms
    radii = np.ones((num_total_atoms, 3))
    rgb = np.ones((num_total_atoms, 3))

    for i in range(num_total_atoms):
        radii[i] = np.repeat(table.atomic_radius(all_atomic_numbers[i], 'VDW'),
                             3)
        rgb[i] = table.atom_color(all_atomic_numbers[i])

    Rgb = numpy_to_vtk(num_array=rgb,
                       deep=True,
                       array_type=ccvtk.VTK_UNSIGNED_CHAR)
    Rgb.SetName("rgb_colors")
    output.GetPointData().SetScalars(Rgb)

    Radii = numpy_to_vtk(num_array=radii,
                         deep=True,
                         array_type=ccvtk.VTK_FLOAT)
    Radii.SetName("radius")
    output.GetPointData().SetVectors(Radii)

    # setting the coordinates
    points = numpy_to_vtk_points(coords)
    output.SetPoints(points)

    ribbonFilter = dcvtk.vtkProteinRibbonFilter()
    ribbonFilter.SetInputData(output)
    ribbonFilter.SetCoilWidth(0.2)
    ribbonFilter.SetDrawSmallMoleculesAsSpheres(0)
    mapper = rcvtk.vtkPolyDataMapper()
    mapper.SetInputConnection(ribbonFilter.GetOutputPort())
    molecule_actor = rcvtk.vtkActor()
    molecule_actor.SetMapper(mapper)
    return molecule_actor