Example #1
0
    def plot(self):
        """
        Plot the contours.
        """

        import bpy
        import numpy as np
        from skimage import measure
        from blendaviz import colors

        # Check if there is any time array.
        if not self.time is None:
            if not isinstance(self.time, np.ndarray):
                print("Error: time is not a valid array.")
                return -1
            elif self.time.ndim != 1:
                print("Error: time array must be 1d.")
                return -1
            # Determine the time index.
            self.time_index = np.argmin(
                abs(bpy.context.scene.frame_float - self.time))
        else:
            self.time = np.array([0])
            self.time_index = 0

        # Check the validity of the input arrays.
        if not isinstance(self.x, np.ndarray) or not isinstance(self.y, np.ndarray) \
           or not isinstance(self.z, np.ndarray):
            print("Error: x OR y OR z array invalid.")
            return -1
        if not ((self.x.shape[0], self.y.shape[0], self.z.shape[0])
                == self.phi.shape[:3]):
            print("Error: input array shapes invalid.")
            return -1
        if not self.psi is None:
            if not isinstance(self.psi, np.ndarray):
                print("Error: psi is not a numpy array.")
                return -1
            if not self.psi.shape[:3] == self.phi.shape[:3]:
                print("Error: psi and phi must of of the same shape.")

        # Point the local variables to the correct arrays.
        if self.x.ndim == 2:
            self._x = self.x[:, self.time_index]
        else:
            self._x = self.x
        if self.y.ndim == 2:
            self._y = self.y[:, self.time_index]
        else:
            self._y = self.y
        if self.z.ndim == 2:
            self._z = self.z[:, self.time_index]
        else:
            self._z = self.z
        if self.phi.ndim == 4:
            self._phi = self.phi[:, :, :, self.time_index]
        else:
            self._phi = self.phi
        if not self.psi is None:
            if self.psi.ndim == 4:
                self._psi = self.psi[:, :, :, self.time_index]
            else:
                self._psi = self.psi
        else:
            self._psi = None

        # Prepare the isosurface levels.
        if isinstance(self.contours, int):
            level_list = np.linspace(self._phi.min(), self._phi.max(),
                                     self.contours + 2)[1:-1]
        elif isinstance(self.contours, list):
            level_list = np.array(self.contours)
        elif isinstance(self.contours, np.ndarray):
            level_list = self.contours.ravel()
        else:
            print("Error: countours invalid. \
                  Must be either integer or 1d array/list.")
            return -1

        # Prepare the material colors.
        color_rgba = colors.make_rgba_array(self.color, level_list.shape[0],
                                            self.color_map, self.vmin,
                                            self.vmax)

        # Determine the grid spacing.
        dx = np.partition(np.array(list(set(list(self._x.ravel())))), 1)[1] - \
             self._x.min()
        dy = np.partition(np.array(list(set(list(self._y.ravel())))), 1)[1] - \
             self._y.min()
        dz = np.partition(np.array(list(set(list(self._z.ravel())))), 1)[1] - \
             self._z.min()

        # Delete existing meshes.
        if not self.mesh_object is None:
            bpy.ops.object.select_all(action='DESELECT')
            for mesh_object in self.mesh_object:
                mesh_object.select_set(state=True)
                bpy.ops.object.delete()
            self.mesh_object = None

        # Delete existing materials.
        if not self.mesh_material is None:
            for mesh_material in self.mesh_material:
                bpy.data.materials.remove(mesh_material)

        # Prepare the lists of mashes and materials.
        self.mesh_data = []
        self.mesh_object = []
        self.mesh_material = []

        for idx, level in enumerate(level_list):
            # Find the vertices and faces of the isosurfaces.
            vertices, faces = measure.marching_cubes_classic(self._phi,
                                                             level,
                                                             spacing=(dx, dy,
                                                                      dz))
            vertices[:, 0] += self._x.min()
            vertices[:, 1] += self._y.min()
            vertices[:, 2] += self._z.min()

            # Create mesh and object.
            self.mesh_data.append(bpy.data.meshes.new("DataMesh"))
            self.mesh_object.append(
                bpy.data.objects.new("ObjMesh", self.mesh_data[-1]))

            # Create mesh from the given data.
            self.mesh_data[-1].from_pydata(list(vertices), [], list(faces))
            self.mesh_data[-1].update(calc_edges=True)

            # Set the material/color.
            if self._psi is None:
                self.__set_material(idx, color_rgba, len(level_list))
            else:
                self.__color_vertices(idx, vertices)
            self.mesh_data[-1].materials.append(self.mesh_material[-1])

            # Link the mesh object with the scene.
            bpy.context.scene.collection.objects.link(self.mesh_object[-1])

        # Group the meshes together.
        for mesh in self.mesh_object[::-1]:
            mesh.select_set(state=True)
            bpy.context.view_layer.objects.active = mesh
        bpy.ops.object.join()
        self.mesh_object = bpy.context.object
        self.mesh_object.select_set(state=False)

        # Make the grouped meshes the deletable object.
        self.deletable_object = self.mesh_object

        return 0
Example #2
0
    def plot(self):
        """
        Plot the arrows.
        """

        import bpy
        import numpy as np
        from mathutils import Vector
        from blendaviz import colors

        # Check if there is any time array.
        if not self.time is None:
            if not isinstance(self.time, np.ndarray):
                print("Error: time is not a valid array.")
                return -1
            elif self.time.ndim != 1:
                print("Error: time array must be 1d.")
                return -1
            # Determine the time index.
            self.time_index = np.argmin(
                abs(bpy.context.scene.frame_float - self.time))
        else:
            self.time = np.array([0])
            self.time_index = 0

        # Check the validity of the input arrays.
        if not isinstance(self.x, np.ndarray) or not isinstance(self.y, np.ndarray) \
           or not isinstance(self.z, np.ndarray):
            print("Error: x OR y OR z array invalid.")
            return -1
        if not isinstance(self.u, np.ndarray) or not isinstance(self.v, np.ndarray) \
           or not isinstance(self.w, np.ndarray):
            print("Error: u OR v OR w array invalid.")
            return -1
        if not (self.x.shape == self.y.shape == self.z.shape == \
                self.u.shape == self.v.shape == self.w.shape):
            print("Error: input array shapes invalid.")
            return -1


# Perhaps it is better not to have these as arrays.
#        # Check the shape of the optional arrays.
#        if isinstance(self.length, np.ndarray):
#            if not (self.x.shape == self.length.shape):
#                print("Error: length array invalid.")
#                return -1
#            else:
#                self.length = self.length.ravel()
#        if isinstance(self.radius_shaft, np.ndarray):
#            if not (self.x.shape == self.radius_shaft.shape):
#                print("Error: radius_shaft array invalid.")
#                return -1
#            else:
#                self.radius_shaft = self.radius_shaft.ravel()
#        if isinstance(self.radius_tip, np.ndarray):
#            if not (self.x.shape == self.radius_tip.shape):
#                print("Error: radius_tip array invalid.")
#                return -1
#            else:
#                self.radius_tip = self.radius_tip.ravel()

# Point the local variables to the correct arrays.
        arrays_with_time_list = ['x', 'y', 'z', 'u', 'v', 'w']
        for array_with_time in arrays_with_time_list:
            array_value = getattr(self, array_with_time)
            if array_value.ndim == 3:
                setattr(self, '_' + array_with_time, array_value.ravel())
            else:
                setattr(self, '_' + array_with_time,
                        array_value[..., self.time_index].ravel())

        # Delete existing meshes.
        if not self.arrow_mesh is None:
            bpy.ops.object.select_all(action='DESELECT')
            self.arrow_mesh.select_set(True)
            bpy.ops.object.delete()
            self.arrow_mesh = None
        self.arrow_mesh = []

        # Delete existing materials.
        if not self.mesh_material is None:
            if isinstance(self.mesh_material, list):
                for mesh_material in self.mesh_material:
                    bpy.data.materials.remove(mesh_material)
            else:
                bpy.data.materials.remove(self.mesh_material)

        # Prepare the material colors.
        if isinstance(self.color, str):
            if self.color == 'magnitude':
                self.color = np.sqrt(self._u**2 + self._v**2 + self._w**2)
        color_rgba = colors.make_rgba_array(self.color, self._x.shape[0],
                                            self.color_map, self.vmin,
                                            self.vmax)

        # Prepare the materials list.
        self.mesh_material = []

        # Plot the arrows.
        for idx in range(self._x.shape[0]):
            # Determine the length of the arrow.
            magnitude = np.sqrt(self._u[idx]**2 + self._v[idx]**2 +
                                self._w[idx]**2)
            normed = np.array([self._u[idx], self._v[idx], self._w[idx]
                               ]) / magnitude
            rotation = Vector((0, 0, 1)).rotation_difference(
                [self._u[idx], self._v[idx], self._w[idx]]).to_euler()

            # Define the arrow's length.
            if self.length == 'magnitude':
                length = magnitude
            else:
                length = self.length
            length *= self.scale

            # Define the arrows' radii.
            radius_shaft = self.radius_shaft
            radius_shaft *= self.scale

            # Define the arrows' scale.
            radius_tip = self.radius_tip
            radius_tip *= self.scale

            if self.pivot == 'tail':
                location = [
                    self._x[idx] + length * normed[0] / 2,
                    self._y[idx] + length * normed[1] / 2,
                    self._z[idx] + length * normed[2] / 2
                ]
            if self.pivot == 'tip':
                location = [
                    self._x[idx] - length * normed[0] / 2,
                    self._y[idx] - length * normed[1] / 2,
                    self._z[idx] - length * normed[2] / 2
                ]
            if self.pivot == 'mid' or self.pivot == 'middle':
                location = [self._x[idx], self._y[idx], self._z[idx]]
            location = np.array(location)

            # Construct the arrow using a cylinder and cone.
            bpy.ops.mesh.primitive_cylinder_add(radius=radius_shaft,
                                                depth=length / 2,
                                                location=location -
                                                normed * length / 4,
                                                rotation=rotation)
            self.arrow_mesh.append(bpy.context.object)
            bpy.ops.mesh.primitive_cone_add(radius1=radius_tip,
                                            radius2=0,
                                            depth=length / 2,
                                            location=location +
                                            normed * length / 4,
                                            rotation=rotation)
            self.arrow_mesh.append(bpy.context.object)

            self.__set_material(idx, color_rgba)

        # Group the meshes together.
        for mesh in self.arrow_mesh[::-1]:
            mesh.select_set(state=True)
        bpy.ops.object.join()
        self.arrow_mesh = bpy.context.object
        self.arrow_mesh.select_set(state=False)

        # Make the mesh the deletable object.
        self.deletable_object = self.arrow_mesh

        return 0
Example #3
0
    def plot(self):
        """
        Plot the streamlines.
        """

        import bpy
        from blendaviz import colors

        # Delete existing curves.
        bpy.ops.object.select_all(action='DESELECT')
        if self.mesh is not None:
            bpy.ops.object.select_all(action='DESELECT')
            self.mesh.select_set(True)
            bpy.ops.object.delete()
            self.curve_data = None
            self.curve_object = None

        # Delete existing materials.
        if self.mesh_material is not None:
            bpy.ops.object.select_all(action='DESELECT')
            for mesh_material in self.mesh_material:
                bpy.data.materials.remove(mesh_material)
            self.mesh_material = None

        # Prepare the seeds.
        self.__generate_seed_points()

        # Prepare the material colors.
        if self.color_scalar is None:
            color_rgba = colors.make_rgba_array(self.color, self.n_seeds,
                                                self.color_map, self.vmin,
                                                self.vmax)

        self.prepare_field_function()

        # Empty the tracers before calculating new.
        self.tracers = []

        if self.n_proc == 1:
            # Compute the traces serially
            for tracer_idx in range(self.n_seeds):
                self.tracers.append(self.__tracer(xx=self.seeds[tracer_idx]))
        else:
            # Compute the positions along the streamlines.
            import multiprocessing as mp
            queue = mp.Queue()
            processes = []
            results = []

            for i_proc in range(self.n_proc):
                processes.append(
                    mp.Process(target=self.__tracer_multi,
                               args=(queue, i_proc, self.n_proc)))
            for i_proc in range(self.n_proc):
                processes[i_proc].start()
            for i_proc in range(self.n_proc):
                results.append(queue.get())
            for i_proc in range(self.n_proc):
                processes[i_proc].join()

            # set the record straight
            result_order = []
            for i_proc in range(self.n_proc):
                result_order.append(results[i_proc][1])
            for i in range(self.n_proc):
                ith_result = result_order.index(i)
                self.tracers.extend(results[ith_result][0])  # tracers

        # Plot the streamlines/tracers.
        self.curve_data = []
        self.curve_object = []
        self.poly_line = []
        self.mesh_material = []
        self.mesh_texture = []
        for tracer_idx in range(self.n_seeds):
            self.curve_data.append(
                bpy.data.curves.new('DataCurve', type='CURVE'))
            self.curve_data[-1].dimensions = '3D'
            self.curve_object.append(
                bpy.data.objects.new('ObjCurve', self.curve_data[-1]))

            # Set the origin to the last point.
            self.curve_object[-1].location = tuple(
                (self.tracers[tracer_idx][-1, 0],
                 self.tracers[tracer_idx][-1, 1], self.tracers[tracer_idx][-1,
                                                                           2]))

            # Add the rest of the curve.
            self.poly_line.append(self.curve_data[-1].splines.new('POLY'))
            self.poly_line[-1].points.add(self.tracers[tracer_idx].shape[0])
            for param in range(self.tracers[tracer_idx].shape[0]):
                self.poly_line[-1].points[param].co = (
                    self.tracers[tracer_idx][param, 0] -
                    self.tracers[tracer_idx][-1, 0],
                    self.tracers[tracer_idx][param, 1] -
                    self.tracers[tracer_idx][-1, 1],
                    self.tracers[tracer_idx][param, 2] -
                    self.tracers[tracer_idx][-1, 2], 0)

            # Add 3d structure.
            self.curve_data[-1].splines.data.bevel_depth = self.radius
            self.curve_data[-1].splines.data.bevel_resolution = self.resolution
            self.curve_data[-1].splines.data.fill_mode = 'FULL'

            # Set the material/color.
            if self.color_scalar is None:
                self.__set_material_color(tracer_idx, color_rgba)
            else:
                self.__set_material_texture(tracer_idx)

            # Link the curve object with the scene.
            bpy.context.scene.collection.objects.link(self.curve_object[-1])

        # Group the curves together.
        bpy.ops.object.select_all(
            action='DESELECT')  # deselect any already selected objects
        for curve_object in self.curve_object[::-1]:
            curve_object.select_set(state=True)
            bpy.context.view_layer.objects.active = curve_object
        bpy.ops.object.join()
        self.mesh = bpy.context.selected_objects[0]
        self.mesh.select_set(False)

        # Make the grouped meshes the deletable object.
        self.deletable_object = self.mesh

        return 0
Example #4
0
    def __color_vertices(self, idx, vertices):
        """
        Set the mesh texture.

        call signature:

        __color_vertices(idx, vertices):

        Keyword arguments:

        *idx*:
          Index of the material.

        *vertices*:
          Vertices of the isosurfaces.
        """

        import bpy
        import numpy as np
        from blendaviz import colors

        # Find interpolated values for Psi on the vertex location.
        psi_vertices = np.zeros(vertices.shape[0])
        for vertex_idx in range(vertices.shape[0]):
            vertex = vertices[vertex_idx]
            # Find the xyz indices for the interpolation.
            ix1 = sum(self._x <= vertex[0]) - 1
            iy1 = sum(self._y <= vertex[1]) - 1
            iz1 = sum(self._z <= vertex[2]) - 1
            ix2 = ix1 + 1
            iy2 = iy1 + 1
            iz2 = iz1 + 1
            if ix2 >= self._phi.shape[0]:
                ix2 = self._phi.shape[0]
            if iy2 >= self._phi.shape[1]:
                iy2 = self._phi.shape[1]
            if iz2 >= self._phi.shape[2]:
                iz2 = self._phi.shape[2]
            # Perform a trilinear interpolation.
            psi_vertices[vertex_idx] = np.mean(self._psi[ix1:ix2 + 1,
                                                         iy1:iy2 + 1,
                                                         iz1:iz2 + 1])

        # Generate the colors for the vertices.
        color_rgba = colors.make_rgba_array(psi_vertices, vertices.shape[0],
                                            self.color_map, self.vmin,
                                            self.vmax)

        # Create a vertex color layer for the mesh.
        vcol_layer = self.mesh_object[idx].data.vertex_colors.new()

        # Add a new material.
        self.mesh_material.append(bpy.data.materials.new('material'))
        self.mesh_material[-1].use_nodes = True
        node_tree = self.mesh_material[-1].node_tree
        nodes = node_tree.nodes
        nodes.remove(nodes[1])
        node_diffuse = nodes.new(type='ShaderNodeBsdfDiffuse')
        node_tree.links.new(node_diffuse.outputs['BSDF'],
                            nodes[0].inputs['Surface'])
        node_vertex_shader = nodes.new(type='ShaderNodeVertexColor')
        node_tree.links.new(node_vertex_shader.outputs['Color'],
                            node_diffuse.inputs['Color'])
        node_vertex_shader.layer_name = 'Col'

        # Change the color of the vertices.
        for poly in self.mesh_object[idx].data.polygons:
            for loop_index in poly.loop_indices:
                loop_vert_index = self.mesh_object[idx].data.loops[
                    loop_index].vertex_index
                vcol_layer.data[loop_index].color = color_rgba[loop_vert_index]
Example #5
0
    def plot(self):
        """
        Plot a as a line, tube or shapes.
        """

        import bpy
        import numpy as np
        from blendaviz import colors

        # Check if there is any time array.
        if not self.time is None:
            if not isinstance(self.time, np.ndarray):
                print("Error: time is not a valid array.")
                return -1
            elif self.time.ndim != 1:
                print("Error: time array must be 1d.")
                return -1
            # Determine the time index.
            self.time_index = np.argmin(abs(bpy.context.scene.frame_float - self.time))
        else:
            self.time = np.array([0])
            self.time_index = 0

        # Point the local variables to the correct time index.
        arrays_with_time_list = ['x', 'y', 'z', 'radius', 'rotation_x', 'rotation_y', 'rotation_z']
        for array_with_time in arrays_with_time_list:
            array_value = getattr(self, array_with_time)
            if not isinstance(array_value, np.ndarray):
                setattr(self, '_' + array_with_time, array_value*np.ones(self.x.shape[0]))
            elif array_value.ndim == 1:
                setattr(self, '_' + array_with_time, array_value)
            else:
                setattr(self, '_' + array_with_time, array_value[:, self.time_index])
        
        # Delete existing curve.
        if not self.curve_data is None:
            bpy.data.curves.remove(self.curve_data)
            self.curve_data = None

        # Delete existing meshes.
        self.__delete_meshes__()

        # Delete existing materials.
        self.__delete_materials__()

        # Create the bezier curve.
        if self.marker is None:
            # Transform color string into rgba.
            color_rgba = colors.make_rgba_array(self.color, 1)

            self.curve_data = bpy.data.curves.new('DataCurve', type='CURVE')
            self.curve_data.dimensions = '3D'
            self.curve_object = bpy.data.objects.new('ObjCurve', self.curve_data)

            # Set the origin to the last point.
            self.curve_object.location = tuple((self._x[-1], self._y[-1], self._z[-1]))

            # Add the rest of the curve.
            self.poly_line = self.curve_data.splines.new('POLY')
            self.poly_line.points.add(self._x.shape[0])
            for param in range(self._x.shape[0]):
                self.poly_line.points[param].co = (self._x[param] - self._x[-1],
                                                   self._y[param] - self._y[-1],
                                                   self._z[param] - self._z[-1],
                                                   0)

            # Add 3d structure.
            self.curve_data.splines.data.bevel_depth = self._radius[0]
            self.curve_data.splines.data.bevel_resolution = self.resolution
            self.curve_data.splines.data.fill_mode = 'FULL'

            # Set the material/color.
            self.mesh_material = bpy.data.materials.new('material')
            self.mesh_material.diffuse_color = color_rgba[0]
            self.mesh_material.roughness = self.roughness
            self.curve_object.active_material = self.mesh_material

            # Set the emission.
            if not self.emission is None:
                self.mesh_material.use_nodes = True
                node_tree = self.mesh_material.node_tree
                nodes = node_tree.nodes
                # Remove and Diffusive BSDF node.
                nodes.remove(nodes[1])
                node_emission = nodes.new(type='ShaderNodeEmission')
                # Change the input of the ouput node to emission.
                node_tree.links.new(node_emission.outputs['Emission'],
                                    nodes[0].inputs['Surface'])
                # Adapt emission and color.
                node_emission.inputs['Color'].default_value = color_rgba
                node_emission.inputs['Strength'].default_value = self.emission

            # Link the curve object with the scene.
            bpy.context.scene.collection.objects.link(self.curve_object)

            # Make this curve the object to be deleted.
            self.deletable_object = self.curve_object

        # Transform color string into rgb.
        color_rgba = colors.make_rgba_array(self.color, self._x.shape[0])

        # Plot the markers.
        if not self.marker is None:
            self.marker_mesh = []
        if self.marker == 'cone':
            for idx in range(self._x.shape[0]):
                bpy.ops.mesh.primitive_cone_add(location=(self._x[idx], self._y[idx], self._z[idx]),
                                                radius1=self._radius[idx],
                                                depth=2*self._radius[idx],
                                                rotation=(self._rotation_x[idx],
                                                          self._rotation_y[idx],
                                                          self._rotation_z[idx]))
                self.marker_mesh.append(bpy.context.object)
        if self.marker == 'cube':
            for idx in range(self._x.shape[0]):
                bpy.ops.mesh.primitive_cube_add(location=(self._x[idx], self._y[idx], self._z[idx]),
                                                size=self._radius[idx],
                                                rotation=(self._rotation_x[idx],
                                                          self._rotation_y[idx],
                                                          self._rotation_z[idx]))
                self.marker_mesh.append(bpy.context.object)
        if self.marker == 'cylinder':
            for idx in range(self._x.shape[0]):
                bpy.ops.mesh.primitive_cylinder_add(location=(self._x[idx], self._y[idx], self._z[idx]),
                                                    radius=self._radius[idx],
                                                    depth=2*self._radius[idx],
                                                    rotation=(self._rotation_x[idx],
                                                              self._rotation_y[idx],
                                                              self._rotation_z[idx]))
                self.marker_mesh.append(bpy.context.object)
        if self.marker == 'ico_sphere':
            for idx in range(self._x.shape[0]):
                bpy.ops.mesh.primitive_ico_sphere_add(location=(self._x[idx], self._y[idx], self._z[idx]),
                                                      radius=self._radius[idx],
                                                      rotation=(self._rotation_x[idx],
                                                                self._rotation_y[idx],
                                                                self._rotation_z[idx]))
                self.marker_mesh.append(bpy.context.object)
        if self.marker == 'monkey':
            for idx in range(self._x):
                bpy.ops.mesh.primitive_monkey_add(location=(self._x[idx], self._y[idx], self._z[idx]),
                                                  size=self._radius[idx],
                                                  rotation=(self._rotation_x[idx],
                                                            self._rotation_y[idx],
                                                            self._rotation_z[idx]))
                self.marker_mesh.append(bpy.context.object)
        if self.marker == 'torus':
            for idx in range(self._x.shape[0]):
                bpy.ops.mesh.primitive_torus_add(location=(self._x[idx], self._y[idx], self._z[idx]),
                                                 major_radius=self._radius[idx],
                                                 minor_radius=0.25*self._radius[idx],
                                                 abso_major_rad=1.25*self._radius[idx],
                                                 abso_minor_rad=0.75*self._radius[idx],
                                                 rotation=(self._rotation_x[idx],
                                                           self._rotation_y[idx],
                                                           self._rotation_z[idx]))
                self.marker_mesh.append(bpy.context.object)
        if self.marker == 'uv_sphere':
            for idx in range(self._x.shape[0]):
                bpy.ops.mesh.primitive_uv_sphere_add(location=(self._x[idx], self._y[idx], self._z[idx]),
                                                     radius=self._radius[idx],
                                                     rotation=(self._rotation_x[idx],
                                                               self._rotation_y[idx],
                                                               self._rotation_z[idx]))
                self.marker_mesh.append(bpy.context.object)
        if isinstance(self.marker, bpy.types.Object):
            if self.marker.type == 'MESH':
                bpy.context.object.select = False
                self.marker.select = True
                for idx in range(self._x.shape[0]):
                    bpy.ops.object.duplicate_move(OBJECT_OT_duplicate={"linked":False, "mode":'TRANSLATION'})
                    bpy.context.object.location = (self._x[idx], self._y[idx], self._z[idx])
                    bpy.context.object.rotation_euler = (self._rotation_x[idx], self._rotation_y[idx], self._rotation_z[idx])
                    self.marker.select = False
                    self.marker_mesh.append(bpy.context.object)

        # Set the material and color.
        if not self.marker is None:
            color_is_array = False
            if isinstance(color_rgba, np.ndarray):
                if color_rgba.ndim == 2:
                    color_is_array = True

            if any([color_is_array,
                    isinstance(self.roughness, np.ndarray),
                    isinstance(self.emission, np.ndarray)]):
                self.mesh_material = []

                for idx in range(self._x.shape[0]):
                    self.mesh_material.append(bpy.data.materials.new('material'))

                    if color_is_array:
                        self.mesh_material[idx].diffuse_color = tuple(color_rgba[idx])
                    else:
                        self.mesh_material[idx].diffuse_color = color_rgba

                    if isinstance(self.roughness, np.ndarray):
                        self.mesh_material[idx].roughness = self.roughness[idx]
                    else:
                        self.mesh_material[idx].roughness = self.roughness

                    if isinstance(self.emission, np.ndarray):
                        self.mesh_material[idx].use_nodes = True
                        node_tree = self.mesh_material[idx].node_tree
                        nodes = node_tree.nodes
                        # Remove and Diffusive BSDF node.
                        nodes.remove(nodes[1])
                        node_emission = nodes.new(type='ShaderNodeEmission')
                        # Change the input of the ouput node to emission.
                        node_tree.links.new(node_emission.outputs['Emission'],
                                            nodes[0].inputs['Surface'])
                        # Adapt emission and color.
                        if color_is_array:
                            node_emission.inputs['Color'].default_value = tuple(color_rgba[idx])
                        else:
                            node_emission.inputs['Color'].default_value = color_rgba
                        node_emission.inputs['Strength'].default_value = self.emission[idx]

                    self.marker_mesh[idx].active_material = self.mesh_material[idx]
            else:
                self.mesh_material = bpy.data.materials.new('material')
                self.mesh_material.diffuse_color = color_rgba
                self.mesh_material.roughness = self.roughness

                if not self.emission is None:
                    self.mesh_material.use_nodes = True
                    node_tree = self.mesh_material.node_tree
                    nodes = node_tree.nodes
                    # Remove and Diffusive BSDF node.
                    nodes.remove(nodes[1])
                    node_emission = nodes.new(type='ShaderNodeEmission')
                    # Change the input of the ouput node to emission.
                    node_tree.links.new(node_emission.outputs['Emission'],
                                        nodes[0].inputs['Surface'])
                    # Adapt emission and color.
                    node_emission.inputs['Color'].default_value = color_rgba
                    node_emission.inputs['Strength'].default_value = self.emission

                for idx, mesh in enumerate(self.marker_mesh):
                    mesh.active_material = self.mesh_material

        # Group the meshes together.
        if not self.marker is None:
            for mesh in self.marker_mesh[::-1]:
                mesh.select_set(state=True)
            bpy.ops.object.join()
            self.marker_mesh = bpy.context.object
            self.marker_mesh.select_set(state=False)
            # Make the grouped meshes the deletable object.
            self.deletable_object = self.marker_mesh

        self.update_globals()

        return 0