Ejemplo n.º 1
0
    def load_bbp_morphology_from_gid(blue_config, gid):
        """This function returns a reference to a morphology in a circuit given by its gid.

        :param blue_config:
            A given BBP circuit configuration file.
        :param gid:
            A given neuron GID.
        :return:
            A reference to a BBP morphology structure
        """

        # Import BluePy
        try:
            import bluepy.v2
        except ImportError:
            print('ERROR: Cannot import [BluePy], please install it')
            return None

        # Loading a circuit
        from bluepy.v2 import Circuit
        circuit = Circuit(blue_config)

        # Get the morphology from its GID
        bbp_morphology = circuit.morph.get(int(gid), True)

        # Return a reference to the morphology
        return bbp_morphology
Ejemplo n.º 2
0
def get_transformation_matrix(blue_config,
                              gid):
    """Get the transformation matrix that correspond to a given neuron with a specific GID.

    :param blue_config:
        A given BBP circuit configuration.
    :param gid:
        Neuron GID.
    :return:
        Transformation matrix.

    """

    # Import BluePy
    try:
        import bluepy.v2
    except ImportError:
        print('ERROR: Cannot import [BluePy], please install it')
        return None

    # Loading a circuit
    from bluepy.v2 import Circuit
    circuit = Circuit(blue_config)

    # Get the neuron
    neuron = circuit.cells.get(int(gid))

    # Translation
    translation = Vector((neuron['x'], neuron['y'], neuron['z']))

    # Orientation
    o = neuron['orientation']
    o0 = Vector((o[0][0], o[0][1], o[0][2]))
    o1 = Vector((o[1][0], o[1][1], o[1][2]))
    o2 = Vector((o[2][0], o[2][1], o[2][2]))

    # Initialize the transformation matrix to I
    transformation_matrix = Matrix()

    transformation_matrix[0][0] = o0[0]
    transformation_matrix[0][1] = o0[1]
    transformation_matrix[0][2] = o0[2]
    transformation_matrix[0][3] = translation[0]

    transformation_matrix[1][0] = o1[0]
    transformation_matrix[1][1] = o1[1]
    transformation_matrix[1][2] = o1[2]
    transformation_matrix[1][3] = translation[1]

    transformation_matrix[2][0] = o2[0]
    transformation_matrix[2][1] = o2[1]
    transformation_matrix[2][2] = o2[2]
    transformation_matrix[2][3] = translation[2]

    transformation_matrix[2][0] = 0.0
    transformation_matrix[2][1] = 0.0
    transformation_matrix[2][2] = 0.0
    transformation_matrix[2][3] = 1.0

    return transformation_matrix
Ejemplo n.º 3
0
    def get_gids_from_target(blue_config, target):
        """Gets a list of GIDs from a target file of a certain circuit.

        :param blue_config:
            A given BBP circuit file.
        :param target:
            Target, a group of GIDs.
        :return:
            A list of GIDs composing the target.
        """

        # Import BluePy
        try:
            import bluepy.v2
        except ImportError:
            print('ERROR: Cannot import [BluePy], please install it')
            return None

        # Loading a circuit
        from bluepy.v2 import Circuit
        circuit = Circuit(blue_config)

        # Loading the GIDs of the sample target within the circuit
        gids = circuit.cells.ids(target)

        # Return a list of all the GIDs
        return gids
Ejemplo n.º 4
0
    def get_neuron_from_gid(blue_config, gid):
        """Return a BBP neuron structure from its input GID.

        :param blue_config:
            Input circuit configuration file.
        :param gid:
            Input neuron GID.
        :return:
            A reference to the BBP neuron
        """

        # Import BluePy
        try:
            import bluepy.v2
        except ImportError:
            print('ERROR: Cannot import [BluePy], please install it')
            return None

        # Loading a circuit
        from bluepy.v2 import Circuit
        circuit = Circuit(blue_config)

        # Get a reference to the neuron where you can access its data later
        bbp_neuron = circuit.cells.get(gid)

        # Return a reference to the BBP neuron
        return bbp_neuron
Ejemplo n.º 5
0
    def load_bbp_morphology_from_gid_using_h5_file(blue_config, gid):
        """This function returns a reference to a morphology in a circuit given by its gid.

        :param blue_config:
            A given BBP circuit configuration file.
        :param gid:
            A given neuron GID.
        :return:
            A reference to a BBP morphology structure
        """

        # Import BluePy
        try:
            import bluepy.v2
        except ImportError:
            print('ERROR: Cannot import [BluePy], please install it')
            return None

        # Loading a circuit
        from bluepy.v2 import Circuit
        circuit = Circuit(blue_config)

        # Get the morphology file path from its GID
        # We must ensure that the GID is integer, that's why the cast is there
        h5_morphology_path = circuit.morph.get_filepath(int(gid))

        # Use the H5 morphology loader to load this file
        # Don't center the morphology, as it is assumed to be cleared and reviewed by the team
        h5_reader = nmv.file.H5Reader(h5_file=h5_morphology_path,
                                      center_morphology=False)
        morphology_file = h5_reader.read_file()

        # Adjust the label to be set according to the GID not the morphology label
        morphology_file.label = str(gid)

        # Return a reference to the morphology
        return morphology_file
Ejemplo n.º 6
0
def run_cluster_neuromorphovis(arguments):
    """Run the NeuroMorphoVis framework on the BBP visualization cluster using SLURM.

    :param arguments:
        Command line arguments.
    """

    # Use a specific circuit target
    if arguments.input == 'target':

        # Log
        print('Loading a target [%s] in circuit [%s]' % (arguments.target, arguments.blue_config))

        # Ensure a valid blue config and a target
        if arguments.blue_config is None or arguments.target is None:
            print('ERROR: Empty circuit configuration file or target')
            exit(0)

        # Import BluePy
        try:
            import bluepy.v2
        except ImportError:
            print('ERROR: Cannot import [BluePy], please install it')
            exit(0)

        from bluepy.v2 import Circuit

        # Loading a circuit
        circuit = Circuit(arguments.blue_config)

        # Loading the GIDs of the sample target within the circuit
        gids = circuit.cells.ids(arguments.target)

        # Run the jobs on the cluster
        slurm.run_gid_jobs_on_cluster(arguments=arguments, gids=gids)

    # Use a single GID
    elif arguments.input == 'gid':

        print('Loading a gid [%s] in circuit [%s]' % (str(arguments.gid), arguments.blue_config))

        # Ensure a valid blue config and a GID
        if arguments.blue_config is None or arguments.gid is None:
            print('ERROR: Empty circuit configuration file or GID')
            exit(0)

        # Run the jobs on the cluster
        slurm.run_gid_jobs_on_cluster(arguments=arguments, gids=[str(arguments.gid)])

    # Use the morphology file (.H5 or .SWC)
    elif arguments.input == 'file':

        # Get the arguments string list
        arguments_string = arguments_parser.get_arguments_string(arguments=arguments)

        # Run the job on the cluster
        slurm.run_morphology_files_jobs_on_cluster(
            arguments=arguments, morphology_files=arguments.morphology_file)

    # Operate on a directory
    elif arguments.input == 'directory':

        # Get all the morphology files in this directory
        morphology_files = file_ops.get_files_in_directory(arguments.morphology_directory, '.h5')

        # Run the jobs on the cluster
        slurm.run_morphology_files_jobs_on_cluster(
            arguments=arguments, morphology_files=morphology_files)

    else:
        print('ERROR: Input data source, use [file, gid, target or directory]')
        exit(0)
Ejemplo n.º 7
0
def run_local_neuromorphovis(arguments):
    """Run the framework on a local node, basically your machine.

    :param arguments:
        Command line arguments.
    """

    # Use a specific circuit target
    if arguments.input == 'target':

        # Log
        print('Loading a target [%s] in circuit [%s]' % (arguments.target, arguments.blue_config))

        # Ensure a valid blue config and a target
        if arguments.blue_config is None or arguments.target is None:
            print('ERROR: Empty circuit configuration file or target')
            exit(0)

        # Import BluePy
        try:
            import bluepy.v2
        except ImportError:
            print('ERROR: Cannot import [BluePy], please install it')
            exit(0)

        from bluepy.v2 import Circuit

        # Loading a circuit
        circuit = Circuit(arguments.blue_config)

        # Loading the GIDs of the sample target within the circuit
        gids = circuit.cells.ids(arguments.target)

        shell_commands = list()
        for gid in gids:

            # Get the argument string for an individual file
            arguments_string = arguments_parser.get_arguments_string_for_individual_gid(
                arguments=arguments, gid=gid)

            # Construct the shell command to run the workflow
            shell_commands.extend(
                create_shell_commands_for_local_execution(arguments, arguments_string))

        # Run NeuroMorphoVis from Blender in the background mode
        for shell_command in shell_commands:
            subprocess.call(shell_command, shell=True)


    # Use a single GID
    elif arguments.input == 'gid':

        print('Loading a gid [%s] in circuit [%s]' % (str(arguments.gid), arguments.blue_config))

        # Ensure a valid blue config and a GID
        if arguments.blue_config is None or arguments.gid is None:
            print('ERROR: Empty circuit configuration file or GID')
            exit(0)

            # Get the argument string for an individual file
            arguments_string = arguments_parser.get_arguments_string_for_individual_gid(
                arguments=arguments, gid=arguments.gid)

            # Construct the shell command to run the workflow
            shell_commands = create_shell_commands_for_local_execution(arguments, arguments_string)

            # Run NeuroMorphoVis from Blender in the background mode
            for shell_command in shell_commands:
                subprocess.call(shell_command, shell=True)

        # Run the jobs on the cluster
        slurm.run_gid_jobs_on_cluster(arguments=arguments, gids=[str(arguments.gid)])

    # Load morphology files (.H5 or .SWC)
    elif arguments.input == 'file':

        # Get the arguments string list
        arguments_string = arguments_parser.get_arguments_string(arguments=arguments)

        # NOTE: Using a morphology file, either in .H5 or .SWC formats is straightforward and
        # therefore the arguments will not change at all. In this case it is safe to pass the
        # arguments as they were received without any change.

        # Construct the shell command to run the workflow
        shell_commands = create_shell_commands_for_local_execution(arguments, arguments_string)

        # Run NeuroMorphoVis from Blender in the background mode
        for shell_command in shell_commands:
            subprocess.call(shell_command, shell=True)

    # Load a directory morphology files (.H5 or .SWC)
    elif arguments.input == 'directory':

        # Get all the morphology files in this directory
        morphology_files = file_ops.get_files_in_directory(arguments.morphology_directory, '.h5')
        morphology_files.extend(file_ops.get_files_in_directory(
            arguments.morphology_directory, '.swc'))

        # If the directory is empty, give an error message
        if len(morphology_files) == 0:
            print('ERROR: The directory [%s] does NOT contain any morphology files' %
                  arguments.morphology_directory)

        # A list of all the commands to be executed
        shell_commands = list()

        # Construct the commands for every individual morphology file
        for morphology_file in morphology_files:

            # Get the argument string for an individual file
            arguments_string = arguments_parser.get_arguments_string_for_individual_file(
                arguments=arguments, morphology_file=morphology_file)

            # Construct the shell command to run the workflow
            shell_commands.extend(
                create_shell_commands_for_local_execution(arguments, arguments_string))

        # Run NeuroMorphoVis from Blender in the background mode
        for shell_command in shell_commands:

            print('RUNNING: **********************************************************************')
            print(shell_command)
            print('*******************************************************************************')
            subprocess.call(shell_command, shell=True)

    else:
        print('ERROR: Input data source, use \'file, gid, target or directory\'')
        exit(0)
Ejemplo n.º 8
0
def create_synaptic_pathway_scene_with_mesh_components(circuit_config,
                                                       pre_gid,
                                                       post_gid,
                                                       output_directory,
                                                       synapses_color=Vector((255, 255, 0)),
                                                       synapse_size=4.0,
                                                       shader=nmv.enums.Shader.FLAT):

    # Loading a circuit
    circuit = Circuit(circuit_config)

    # Clear the scene
    nmv.scene.clear_scene()

    pre_meshes_list = create_neuron_meshes_with_piecewise_builder(
        circuit=circuit, gid=pre_gid, with_axon=True)

    # Adjust the transformation
    pre_transformation = circuit_data.get_cell_transformation(circuit, pre_gid)
    for mesh in pre_meshes_list:
        mesh.matrix_world = pre_transformation

    # Clear all the lights
    clear_lights()

    # Clear all materials
    clear_materials()

    # Export the scene into a blend file
    nmv.file.export_scene_to_blend_file(output_directory, str(pre_gid))
    nmv.file.export_mesh_objects_to_file(mesh_objects=pre_meshes_list,
                                         output_directory=output_directory,
                                         output_file_name=str(pre_gid),
                                         file_format=nmv.enums.Meshing.ExportFormat.OBJ,
                                         export_individual_meshes=True)

    # Clear the scene in Blender
    nmv.scene.clear_scene()

    # Create the post-synaptic mesh
    post_meshes_list = create_neuron_meshes_with_piecewise_builder(circuit=circuit, gid=post_gid)
    print(post_meshes_list)

    # Adjust the neuron transformation
    post_transformation = circuit_data.get_cell_transformation(circuit, post_gid)
    for mesh in post_meshes_list:
        mesh.matrix_world = post_transformation

    # Clear all the lights
    clear_lights()

    # Clear all materials
    clear_materials()

    # Export the scene into a blend file
    nmv.file.export_scene_to_blend_file(output_directory, str(post_gid))
    nmv.file.export_mesh_objects_to_file(mesh_objects=post_meshes_list,
                                         output_directory=output_directory,
                                         output_file_name=str(post_gid),
                                         file_format=nmv.enums.Meshing.ExportFormat.OBJ,
                                         export_individual_meshes=True)

    # For consistency, export the pair into a pre-pair blend file
    nmv.file.export_scene_to_blend_file(output_directory, '%d_%d' % (pre_gid, post_gid))

    # Clear the scene again
    nmv.scene.clear_scene()

    # Import both meshes
    # nmv.file.import_object_from_blend_file(output_directory, str(pre_gid) + '.blend')
    # nmv.file.import_object_from_blend_file(output_directory, str(post_gid) + '.blend')

    # Create the synapse material
    synapses_mesh = create_shared_synapses_mesh(circuit, pre_gid, post_gid,
                                                synapses_color, synapse_size, shader)

    # Import the pre-synaptic meshes
    pre_synaptic_neuron_meshes_directory = '%s/%s' % (output_directory, str(pre_gid))
    pre_mesh_files = nmv.file.get_files_in_directory(directory=pre_synaptic_neuron_meshes_directory,
                                                     file_extension='obj')
    pre_meshes_list = list()
    for mesh_file in pre_mesh_files:
        pre_component = nmv.file.import_obj_file(
            input_directory=pre_synaptic_neuron_meshes_directory, input_file_name=mesh_file)
        pre_component.name = str(pre_gid) + '_' + pre_component.name
        pre_component.rotation_euler[0] = 0
        pre_meshes_list.append(pre_component)

    # Import the post-synaptic meshes
    post_synaptic_neuron_meshes_directory = '%s/%s' % (output_directory, str(post_gid))
    post_mesh_files = nmv.file.get_files_in_directory(
        directory=post_synaptic_neuron_meshes_directory,
        file_extension='obj')

    post_meshes_list = list()
    for mesh_file in post_mesh_files:
        post_component = nmv.file.import_obj_file(
            input_directory=post_synaptic_neuron_meshes_directory, input_file_name=mesh_file)
        post_component.name = str(post_gid) + '_' + post_component.name
        post_component.rotation_euler[0] = 0
        post_meshes_list.append(post_component)

    pre_dendrite_material, pre_axon_material = create_pre_synaptic_neuron_materials()
    for mesh_component in pre_meshes_list:
        if 'Dendrite' in mesh_component.name or 'Soma' in mesh_component.name:
            nmv.shading.set_material_to_object(mesh_object=mesh_component,
                                               material_reference=pre_dendrite_material)
        else:
            nmv.shading.set_material_to_object(mesh_object=mesh_component,
                                               material_reference=pre_axon_material)

    # Select the post-synaptic mesh to assign the color
    post_dendrite_material, post_axon_material = create_post_synaptic_neuron_materials()
    for mesh_component in post_meshes_list:
        nmv.shading.set_material_to_object(mesh_object=mesh_component,
                                           material_reference=post_dendrite_material)

    # Create sun light
    bpy.ops.object.light_add(type='SUN', radius=1.0, location=(0, 0, 0))

    # For consistency, export the pair into a pre-pair blend file
    nmv.file.export_scene_to_blend_file(output_directory, '%d_%d' % (pre_gid, post_gid))

    # Return a reference to the synapse mesh, for the close up rendering
    return synapses_mesh
Ejemplo n.º 9
0
def create_synaptic_pathway_scene(circuit_config,
                                  pre_gid,
                                  post_gid,
                                  output_directory,
                                  synapses_color=Vector((255, 255, 0)),
                                  synapse_size=4.0,
                                  shader=nmv.enums.Shader.FLAT):
    # Loading a circuit
    circuit = Circuit(circuit_config)

    # Clear the scene
    nmv.scene.clear_scene()

    # Create the pre-synaptic mesh
    pre_mesh = create_neuron_mesh(circuit=circuit, gid=pre_gid, with_axon=True)

    # Adjust the transformation
    pre_transformation = circuit_data.get_cell_transformation(circuit, pre_gid)
    pre_mesh.matrix_world = pre_transformation

    # Clear all the lights
    clear_lights()

    # Export the scene into a blend file
    nmv.file.export_scene_to_blend_file(output_directory, str(pre_gid))

    # Clear the scene in Blender
    nmv.scene.clear_scene()

    # Create the post-synaptic mesh
    post_mesh = create_neuron_mesh(circuit=circuit, gid=post_gid)

    # Adjust the neuron transformation
    post_transformation = circuit_data.get_cell_transformation(circuit, post_gid)
    post_mesh.matrix_world = post_transformation

    # Clear all the lights
    clear_lights()

    # Export the scene into a blend file
    nmv.file.export_scene_to_blend_file(output_directory, str(post_gid))

    # For consistency, export the pair into a pre-pair blend file
    nmv.file.export_scene_to_blend_file(output_directory, '%d_%d' % (pre_gid, post_gid))

    # Clear the scene again
    nmv.scene.clear_scene()

    # Import both meshes
    nmv.file.import_object_from_blend_file(output_directory, str(pre_gid) + '.blend')
    nmv.file.import_object_from_blend_file(output_directory, str(post_gid) + '.blend')

    # Create the synapse material
    synapses_mesh = create_shared_synapses_mesh(circuit, pre_gid, post_gid,
                                                synapses_color, synapse_size, shader)

    # Select the pre-synaptic mesh to assign the material
    pre_synaptic_mesh = nmv.scene.get_object_by_name(str(pre_gid))
    pre_material = create_pre_synaptic_neuron_material()

    nmv.shading.set_material_to_object(mesh_object=pre_synaptic_mesh,
                                       material_reference=pre_material)

    # Select the post-synaptic mesh to assign the color
    post_synaptic_mesh = nmv.scene.get_object_by_name(str(post_gid))
    post_material = create_post_synaptic_neuron_material()
    nmv.shading.set_material_to_object(mesh_object=post_synaptic_mesh,
                                       material_reference=post_material)

    # Create sun light
    bpy.ops.object.light_add(type='SUN', radius=2.5, location=(0, 0, 0))

    # Save
    nmv.file.export_scene_to_blend_file(output_directory, str('All'))
Ejemplo n.º 10
0
def create_synaptome(circuit_config,
                     gid,
                     synapse_size,
                     synapse_percentage,
                     synaptome_color_map_materials,
                     neuron_material,
                     show_excitatory_inhibitory=True):
    """Creates the synaptome mesh.

    :param circuit_config:
        BBP circuit config.
    :param gid:
        Neuron GID.
    :param synapse_size:
        The size of the synapse.
    :param synapse_percentage:
        The percentage of the synapses.
    :param synaptome_color_map_materials:
        A list of all the materials of the synapses.
    :param neuron_material:
        The material of the neuron.
    :param show_excitatory_inhibitory:
        If this flag is set to true, we will show the excitatory and inhibitory synapses, otherwise
        we will show the synaptic map according to the mtype of the pre-synaptic pairs.
    :return:
        THe synaptome mesh.
    """

    # Loading a circuit
    circuit = Circuit(circuit_config)

    # Get the cell transformation matrix
    transformation = circuit_data.get_cell_transformation(circuit=circuit, gid=gid)

    # Invert the transformation matrix
    inverted_transformation = transformation.inverted()

    # Neuron mtype
    mtype = circuit.cells.get(gid).mtype

    # Create neuron mesh
    neuron_mesh = create_neuron_mesh(circuit=circuit, gid=gid,
                                     neuron_material=neuron_material)

    # Create synapse mesh
    if show_excitatory_inhibitory:
        synapses_mesh = create_excitatory_inhibitory_synapses_mesh(
            circuit=circuit, gid=gid, synapse_size=synapse_size,
            synapse_percentage=synapse_percentage, inverted_transformation=inverted_transformation,
            color_map_materials=synaptome_color_map_materials)
    else:
        synapses_mesh = create_mtype_based_synapses_mesh(
            circuit=circuit, gid=gid, synapse_size=synapse_size,
            synapse_percentage=synapse_percentage, inverted_transformation=inverted_transformation,
            color_map_materials=synaptome_color_map_materials)

    # Merge
    synaptome_mesh = nmv.mesh.join_mesh_objects(
        mesh_list=[neuron_mesh, synapses_mesh], name='synaptome_%s_%d' % (mtype, gid))

    # Returns a reference to the synaptome mesh
    return synaptome_mesh
Ejemplo n.º 11
0
def build_circuit_spines(morphology, blue_config, gid, material=None):
    """Builds all the spines on a spiny neuron using a BBP circuit.

    :param morphology:
        A given morphology.
    :param blue_config:
        BBP circuit configuration file.
    :param gid:
        Neuron gid.
    :param material:
        Spine material.
    :return:
        A list of all the reconstructed spines along the neuron.
    """

    # Keep a list of all the spines objects
    spines_objects = []

    # Loading a circuit
    from bluepy.v2 import Circuit
    circuit = Circuit(blue_config)

    # Get the IDs of the afferent (or incoming) synapses
    synapse_ids = circuit.connectome.afferent_synapses(int(gid))

    # Get the positions of the incoming synapses at the post synaptic side
    post_pos = circuit.connectome.synapse_positions(synapse_ids, 'post',
                                                    'center')

    # The pre-synaptic position
    pre_pos = circuit.connectome.synapse_positions(synapse_ids, 'pre',
                                                   'contour')

    # Get the neuron
    neuron = circuit.cells.get(int(gid))

    # Translation
    translation = Vector((neuron['x'], neuron['y'], neuron['z']))

    # Orientation
    o = neuron['orientation']
    o0 = Vector((o[0][0], o[0][1], o[0][2]))
    o1 = Vector((o[1][0], o[1][1], o[1][2]))
    o2 = Vector((o[2][0], o[2][1], o[2][2]))

    # Initialize the transformation matrix to I
    transformation_matrix = Matrix()

    transformation_matrix[0][0] = o0[0]
    transformation_matrix[0][1] = o0[1]
    transformation_matrix[0][2] = o0[2]
    transformation_matrix[0][3] = translation[0]

    transformation_matrix[1][0] = o1[0]
    transformation_matrix[1][1] = o1[1]
    transformation_matrix[1][2] = o1[2]
    transformation_matrix[1][3] = translation[1]

    transformation_matrix[2][0] = o2[0]
    transformation_matrix[2][1] = o2[1]
    transformation_matrix[2][2] = o2[2]
    transformation_matrix[2][3] = translation[2]

    transformation_matrix[3][0] = 0.0
    transformation_matrix[3][1] = 0.0
    transformation_matrix[3][2] = 0.0
    transformation_matrix[3][3] = 1.0

    # Load all the template spines and ignore the verbose messages of loading
    templates_spines_list = load_spines(
        nmv.consts.Paths.SPINES_MESHES_LQ_DIRECTORY)

    # Invert the transformation matrix
    transformation_matrix = transformation_matrix.inverted()

    # Create a timer to report the performance
    building_timer = nmv.utilities.timer.Timer()

    nmv.logger.header('Building spines')
    building_timer.start()

    # Load the synapses from the file
    number_spines = len(synapse_ids)
    for i, synapse in enumerate(synapse_ids):

        # Show progress
        nmv.utilities.time_line.show_iteration_progress(
            'Spines', i, number_spines)

        # Pre and post synaptic positions in Vectors
        pre_position = Vector((pre_pos['x'][synapse], pre_pos['y'][synapse],
                               pre_pos['z'][synapse]))
        post_position = Vector((post_pos['x'][synapse], post_pos['y'][synapse],
                                post_pos['z'][synapse]))

        # Transform the spine positions to the circuit coordinates
        post_position = transformation_matrix @ post_position
        pre_position = transformation_matrix @ pre_position

        # Emanate a spine
        spine_object = emanate_a_spine(templates_spines_list, post_position,
                                       pre_position, i)

        # Apply the material to the spine object
        if material is not None:
            nmv.shading.set_material_to_object(mesh_object=spine_object,
                                               material_reference=material)

        # Append the spine to spines list
        spines_objects.append(spine_object)

    # Done
    nmv.utilities.time_line.show_iteration_progress('Spines',
                                                    number_spines,
                                                    number_spines,
                                                    done=True)

    # Link the spines to the scene in a single step
    nmv.logger.info('Linking spines to the scene')
    for i in spines_objects:
        nmv.scene.link_object_to_scene(i)

    # Report the time
    building_timer.end()
    nmv.logger.info('Spines: [%f] seconds' % building_timer.duration())

    # Delete the template spines
    nmv.scene.ops.delete_list_objects(templates_spines_list)

    # Return the spines objects list
    return spines_objects