<commit_before>import os
import sys
import vtk
import numpy as np
from vtk.util.colors import black, white, grey, blue, orange
from vtk.util import numpy_support

# see if some of the stuff needs to be moved to the Microstructure module
from pymicro.crystal.lattice import Lattice, HklPlane


def to_vtk_type(type):
    '''Function to get the VTK data type given a numpy data type.

    :param str type: The numpy data type like 'uint8', 'uint16'...
    :return: A VTK data type.
    '''
    if type == 'uint8':
        return vtk.VTK_UNSIGNED_CHAR
    elif type == 'uint16':
        return vtk.VTK_UNSIGNED_SHORT
    elif type == 'uint32':
        return vtk.VTK_UNSIGNED_INT
    elif type == 'float':
        return vtk.VTK_FLOAT
    elif type == 'float64':
        return vtk.VTK_DOUBLE


def rand_cmap(N=256, first_is_black=False, table_range=(0, 255)):
    '''Create a VTK lookup table with random colors.

    The first color can be enforced to black and usually figure out
    the image background. The random seed is fixed to 13 in order
    to consistently produce the same colormap.

    :param int N: The number of colors in the colormap.
    :param bool first_is_black: Force the first color to be black.
    :param typle table_range: The range of the VTK lookup table
    :return: A vtkLookupTable lookup table with N random colors.
    '''
    np.random.seed(13)
    rand_colors = np.random.rand(N, 3)
    if first_is_black:
        rand_colors[0] = [0., 0., 0.]  # enforce black background
    lut = vtk.vtkLookupTable()
    lut.SetNumberOfTableValues(N)
    lut.Build()
    for i in range(N):
        lut.SetTableValue(i, rand_colors[i][0], rand_colors[i][1], rand_colors[i][2], 1.0)
    lut.SetRange(table_range)
    return lut


def pv_rand_cmap(N=256, first_is_black=False):
    '''Write out the random color map in paraview xml format.

    This method print out the XML declaration of the random colormap.
    This may be saved to a text file and used in paraview.

    :param int N: The number of colors in the colormap.
    :param bool first_is_black: Force the first color to be black.
    '''
    np.random.seed(13)
    rand_colors = np.random.rand(N, 3)
    if first_is_black:
        rand_colors[0] = [0., 0., 0.]  # enforce black background
    print('<ColorMap name="random" space="RGB">')
    for i in range(N):
        print('<Point x="%d" o="1" r="%8.6f" g="%8.6f" b="%8.6f"/>' %
              (i, rand_colors[i][0], rand_colors[i][1], rand_colors[i][2]))
    print('</ColorMap>')


def gray_cmap(table_range=(0, 255)):
    '''create a black and white colormap.

    *Parameters*

    **table_range**: 2 values tuple (default: (0,255))
    start and end values for the table range.

    *Returns*

    A vtkLookupTable from black to white.
    '''
    lut = vtk.vtkLookupTable()
    lut.SetSaturationRange(0, 0)
    lut.SetHueRange(0, 0)
    lut.SetTableRange(table_range)
    lut.SetValueRange(0, 1)
    lut.SetRampToLinear()
    lut.Build()
    return lut


def invert_cmap(ref_lut):
    '''invert a VTK lookup table.

    *Parameters*

    **ref_lut**: The lookup table to invert.

    *Returns*

    A reverse vtkLookupTable.
    '''
    N = ref_lut.GetNumberOfTableValues()
    lut = vtk.vtkLookupTable()
    lut.SetNumberOfTableValues(N)
    lut.Build()
    for i in range(N):
        lut.SetTableValue(i, ref_lut.GetTableValue(N - i))
    lut.SetRange(ref_lut.GetTableRange())
    return lut


def hsv_cmap(N=64, table_range=(0, 255)):
    '''Create a VTK look up table similar to matlab's hsv.

    *Parameters*

    **N**: int, number of colors in the table.

    **table_range**: 2 values tuple (default: (0,255))
    start and end values for the table range.

    *Returns*

    A vtkLookupTable.
    '''
    lut = vtk.vtkLookupTable()
    lut.SetHueRange(0.0, 1.0)
    lut.SetSaturationRange(1.0, 1.0)
    lut.SetValueRange(1.0, 1.0)
    lut.SetNumberOfColors(N)
    lut.Build()
    lut.SetRange(table_range)
    return lut


def jet_cmap(N=64, table_range=(0, 255)):
    '''Create a VTK look up table similar to matlab's jet.

    *Parameters*

    **N**: int, number of colors in the table.

    **table_range**: 2 values tuple (default: (0,255))
    start and end values for the table range.

    *Returns*

    A vtkLookupTable from blue to red.
    '''
    lut = vtk.vtkLookupTable()
    lut.SetHueRange(0.667, 0.0)
    lut.SetNumberOfColors(N)
    lut.Build()
    lut.SetRange(table_range)
    return lut


def hot_cmap(table_range=(0, 255)):
    '''Create a VTK look up table similar to matlab's hot.

    *Parameters*

    **table_range**: 2 values tuple (default: (0,255))
    start and end values for the table range.

    *Returns*

    A vtkLookupTable from white to red.
    '''
    lut = vtk.vtkLookupTable()
    lutNum = 64
    lut.SetNumberOfTableValues(lutNum)
    lut.Build()
    lut.SetTableValue(0, 0.041667, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(1, 0.083333, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(2, 0.125000, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(3, 0.166667, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(4, 0.208333, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(5, 0.250000, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(6, 0.291667, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(7, 0.333333, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(8, 0.375000, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(9, 0.416667, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(10, 0.458333, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(11, 0.500000, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(12, 0.541667, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(13, 0.583333, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(14, 0.625000, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(15, 0.666667, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(16, 0.708333, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(17, 0.750000, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(18, 0.791667, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(19, 0.833333, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(20, 0.875000, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(21, 0.916667, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(22, 0.958333, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(23, 1.000000, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(24, 1.000000, 0.041667, 0.000000, 1.0)
    lut.SetTableValue(25, 1.000000, 0.083333, 0.000000, 1.0)
    lut.SetTableValue(26, 1.000000, 0.125000, 0.000000, 1.0)
    lut.SetTableValue(27, 1.000000, 0.166667, 0.000000, 1.0)
    lut.SetTableValue(28, 1.000000, 0.208333, 0.000000, 1.0)
    lut.SetTableValue(29, 1.000000, 0.250000, 0.000000, 1.0)
    lut.SetTableValue(30, 1.000000, 0.291667, 0.000000, 1.0)
    lut.SetTableValue(31, 1.000000, 0.333333, 0.000000, 1.0)
    lut.SetTableValue(32, 1.000000, 0.375000, 0.000000, 1.0)
    lut.SetTableValue(33, 1.000000, 0.416667, 0.000000, 1.0)
    lut.SetTableValue(34, 1.000000, 0.458333, 0.000000, 1.0)
    lut.SetTableValue(35, 1.000000, 0.500000, 0.000000, 1.0)
    lut.SetTableValue(36, 1.000000, 0.541667, 0.000000, 1.0)
    lut.SetTableValue(37, 1.000000, 0.583333, 0.000000, 1.0)
    lut.SetTableValue(38, 1.000000, 0.625000, 0.000000, 1.0)
    lut.SetTableValue(39, 1.000000, 0.666667, 0.000000, 1.0)
    lut.SetTableValue(40, 1.000000, 0.708333, 0.000000, 1.0)
    lut.SetTableValue(41, 1.000000, 0.750000, 0.000000, 1.0)
    lut.SetTableValue(42, 1.000000, 0.791667, 0.000000, 1.0)
    lut.SetTableValue(43, 1.000000, 0.833333, 0.000000, 1.0)
    lut.SetTableValue(44, 1.000000, 0.875000, 0.000000, 1.0)
    lut.SetTableValue(45, 1.000000, 0.916667, 0.000000, 1.0)
    lut.SetTableValue(46, 1.000000, 0.958333, 0.000000, 1.0)
    lut.SetTableValue(47, 1.000000, 1.000000, 0.000000, 1.0)
    lut.SetTableValue(48, 1.000000, 1.000000, 0.062500, 1.0)
    lut.SetTableValue(49, 1.000000, 1.000000, 0.125000, 1.0)
    lut.SetTableValue(50, 1.000000, 1.000000, 0.187500, 1.0)
    lut.SetTableValue(51, 1.000000, 1.000000, 0.250000, 1.0)
    lut.SetTableValue(52, 1.000000, 1.000000, 0.312500, 1.0)
    lut.SetTableValue(53, 1.000000, 1.000000, 0.375000, 1.0)
    lut.SetTableValue(54, 1.000000, 1.000000, 0.437500, 1.0)
    lut.SetTableValue(55, 1.000000, 1.000000, 0.500000, 1.0)
    lut.SetTableValue(56, 1.000000, 1.000000, 0.562500, 1.0)
    lut.SetTableValue(57, 1.000000, 1.000000, 0.625000, 1.0)
    lut.SetTableValue(58, 1.000000, 1.000000, 0.687500, 1.0)
    lut.SetTableValue(59, 1.000000, 1.000000, 0.750000, 1.0)
    lut.SetTableValue(60, 1.000000, 1.000000, 0.812500, 1.0)
    lut.SetTableValue(61, 1.000000, 1.000000, 0.875000, 1.0)
    lut.SetTableValue(62, 1.000000, 1.000000, 0.937500, 1.0)
    lut.SetTableValue(63, 1.000000, 1.000000, 1.000000, 1.0)
    lut.SetRange(table_range)
    return lut


def add_hklplane_to_grain(hkl_plane, grid, orientation, origin=(0, 0, 0),
                          opacity=1.0, show_normal=False, normal_length=1.0):
    # compute the plane normal in the laboratory frame using the grain orientation
    gt = orientation.orientation_matrix().transpose()
    n_rot = np.dot(gt, hkl_plane.normal() / np.linalg.norm(hkl_plane.normal()))
    rot_plane = vtk.vtkPlane()
    rot_plane.SetOrigin(origin)
    # rotate the plane by setting the normal
    rot_plane.SetNormal(n_rot)
    return add_plane_to_grid(rot_plane, grid, opacity=opacity, show_normal=show_normal, normal_length=normal_length)


def add_plane_to_grid(plane, grid, origin=None, color=None, opacity=0.3, show_normal=False, normal_length=1.0):
    '''Add a 3d plane inside another object.

    This function adds a plane inside another object described by a mesh (vtkunstructuredgrid). 
    The method is to use a vtkCutter with the mesh as input and the plane as the cut function. 
    The plane normal can be displayed and its length controlled.
    This may be used directly to add hkl planes inside a lattice cell or
    a grain.

    :param plane: A VTK implicit function describing the plane to add.
    :param grid: A VTK unstructured grid in which the plane is to be added.
    :param tuple origin: (x, y, z) tuple to define the origin of the plane.
    :param tuple color: A color defined by its rgb components.
    :param float opacity: Opacity value of the plane actor.
    :param bool show_normal: A flag to display the plane normal.
    :param normal_length: The length of the plane normal vector (1.0 by default).
    :returns: A VTK actor or assembly with the plane, and its normal if requested.
    '''
    # cut the unstructured grid with the plane
    planeCut = vtk.vtkCutter()
    if vtk.vtkVersion().GetVTKMajorVersion() > 5:
        planeCut.SetInputData(grid)
    else:
        planeCut.SetInput(grid)
    planeCut.SetCutFunction(plane)

    cutMapper = vtk.vtkPolyDataMapper()
    cutMapper.SetInputConnection(planeCut.GetOutputPort())
    cutActor = vtk.vtkActor()
    cutActor.SetMapper(cutMapper)
    if color:
        cutActor.GetProperty().SetColor(color)
    cutActor.GetProperty().SetOpacity(opacity)
    if show_normal:
        # add an arrow to display the normal to the plane
        assembly = vtk.vtkAssembly()
        assembly.AddPart(cutActor)
        if origin is None:
            origin = plane.GetOrigin()
        arrowActor = unit_arrow_3d(origin, normal_length * np.array(plane.GetNormal()), make_unit=False)
        assembly.AddPart(arrowActor)
        return assembly
    else:
        return cutActor

def axes_actor(length=1.0, axisLabels=('x', 'y', 'z'), fontSize=20, color=None):
    '''Build an actor for the cartesian axes.

    :param length: The arrow length of the axes (1.0 by default).
    :type length: float or triple of float to specify the length of each axis individually.
    :param list axisLabels: Specify the axes labels (xyz by default), use axisLabels = None to hide the axis labels
    :param int fontSize: Font size for the axes labels (20 by default).
    :param tuple color: A single color defined by its rgb components (not set by default which keep the red, green, blue colors).
    :returns: A VTK assembly representing the cartesian axes.
    '''
    axes = vtk.vtkAxesActor()
    if isinstance(length, (float, int, np.int64, np.float64)):
        axes.SetTotalLength(length, length, length)
    else:
        assert(len(length) == 3)
        axes.SetTotalLength(length)
    axes.SetShaftTypeToCylinder()
    axes.SetCylinderRadius(0.02)
    if axisLabels:
        axes.SetXAxisLabelText(axisLabels[0])
        axes.SetYAxisLabelText(axisLabels[1])
        axes.SetZAxisLabelText(axisLabels[2])
        axprop = vtk.vtkTextProperty()
        axprop.SetColor(0, 0, 0)
        axprop.SetFontSize(fontSize)
        axprop.SetFontFamilyToArial()
        axes.GetXAxisCaptionActor2D().SetCaptionTextProperty(axprop)
        axes.GetYAxisCaptionActor2D().SetCaptionTextProperty(axprop)
        axes.GetZAxisCaptionActor2D().SetCaptionTextProperty(axprop)
    else:
        axes.SetAxisLabels(0)
    if color:
        # set the color of the whole triad
        collection = vtk.vtkPropCollection()
        axes.GetActors(collection)
        for i in range(collection.GetNumberOfItems()):
            collection.GetItemAsObject(i).GetProperty().SetColor(color)
    return axes


def grain_3d(grain, hklplanes=None, show_normal=False, plane_origins=None,
             plane_opacity=1.0, show_orientation=False, N=2048, verbose=False):
    '''Creates a 3d representation of a crystallographic grain.

    This method creates a vtkActor object of the surface mesh
    representing a Grain object. An optional list of crystallographic
    planes can be given

    :param grain: the Grain object to be shown in 3d.
    :param list hklplanes: the list of HklPlanes object to add to the assembly.
    :param bool show_normal: show also the normal to the hkl planes if True.
    :param list plane_origins: the list of each plane origin (3d scene coordinates).
    :param float plane_opacity: set the opacity of the grain actor.
    :param bool show_orientation: show also the grain orientation with a vtkAxesActor placed at the grain center if True.
    :param int N: the number of colors to use in the colormap.
    :param bool verbose: activate verbose mode.
    :returns: a vtkAssembly of the grain mesh and the optional hkl planes.
    '''
    assembly = vtk.vtkAssembly()
    # create mapper
    mapper = vtk.vtkDataSetMapper()
    if vtk.vtkVersion().GetVTKMajorVersion() > 5:
        mapper.SetInputData(grain.vtkmesh)
    else:
        mapper.SetInput(grain.vtkmesh)
    mapper.ScalarVisibilityOff()  # we use the grain id for chosing the color
    lut = rand_cmap(N, first_is_black=True, table_range=(0, N - 1))
    grain_actor = vtk.vtkActor()
    if verbose:
        print(grain.id)
        print(lut.GetTableValue(grain.id)[0:3])
    grain_actor.GetProperty().SetColor(lut.GetTableValue(grain.id)[0:3])
    grain_actor.GetProperty().SetOpacity(0.3)
    grain_actor.SetMapper(mapper)
    assembly.AddPart(grain_actor)
    # add all hkl planes
    if hklplanes != None:
        for i, hklplane in enumerate(hklplanes):
            # the grain has its center of mass at the origin
            origin = (0., 0., 0.)
            if plane_origins is not None:
                origin = plane_origins[i]  # in unit of the 3d scene
                if verbose:
                    print('using origin %s' % str(origin))
            hklplaneActor = add_hklplane_to_grain(hklplane, grain.vtkmesh,
                                                  grain.orientation, origin, opacity=plane_opacity,
                                                  show_normal=show_normal, normal_length=50.)
            assembly.AddPart(hklplaneActor)
    if show_orientation:
        local_orientation = add_local_orientation_axes(grain.orientation, axes_length=30)
        # add local orientation to the grain actor
        assembly.AddPart(local_orientation)
    return assembly


# deprecated, will be removed soon
def add_grain_to_3d_scene(grain, hklplanes, show_orientation=False):
    assembly = vtk.vtkAssembly()
    # create mapper
    print('creating grain actor')
    mapper = vtk.vtkDataSetMapper()
    if vtk.vtkVersion().GetVTKMajorVersion() > 5:
        mapper.SetInputData(grain.vtkmesh)
    else:
        mapper.SetInput(grain.vtkmesh)
    mapper.ScalarVisibilityOff()  # we use the grain id for choosing the color
    lut = rand_cmap(N=2048, first_is_black=True, table_range=(0, 2047))
    grain_actor = vtk.vtkActor()
    grain_actor.GetProperty().SetColor(lut.GetTableValue(grain.id)[0:3])
    grain_actor.SetMapper(mapper)
    assembly.AddPart(grain_actor)
    # add all hkl planes and local grain orientation actor<commit_msg>add show_intersection args to add_hkl_plane_to_grain method and some cleanup<commit_after>import os
import sys
import vtk
import numpy as np
from vtk.util.colors import red, green, black, white, grey, blue, orange
from vtk.util import numpy_support

# see if some of the stuff needs to be moved to the Microstructure module
from pymicro.crystal.lattice import Lattice, HklPlane


def to_vtk_type(type):
    '''Function to get the VTK data type given a numpy data type.

    :param str type: The numpy data type like 'uint8', 'uint16'...
    :return: A VTK data type.
    '''
    if type == 'uint8':
        return vtk.VTK_UNSIGNED_CHAR
    elif type == 'uint16':
        return vtk.VTK_UNSIGNED_SHORT
    elif type == 'uint32':
        return vtk.VTK_UNSIGNED_INT
    elif type == 'float':
        return vtk.VTK_FLOAT
    elif type == 'float64':
        return vtk.VTK_DOUBLE


def rand_cmap(N=256, first_is_black=False, table_range=(0, 255)):
    '''Create a VTK lookup table with random colors.

    The first color can be enforced to black and usually figure out
    the image background. The random seed is fixed to 13 in order
    to consistently produce the same colormap.

    :param int N: The number of colors in the colormap.
    :param bool first_is_black: Force the first color to be black.
    :param typle table_range: The range of the VTK lookup table
    :return: A vtkLookupTable lookup table with N random colors.
    '''
    np.random.seed(13)
    rand_colors = np.random.rand(N, 3)
    if first_is_black:
        rand_colors[0] = [0., 0., 0.]  # enforce black background
    lut = vtk.vtkLookupTable()
    lut.SetNumberOfTableValues(N)
    lut.Build()
    for i in range(N):
        lut.SetTableValue(i, rand_colors[i][0], rand_colors[i][1], rand_colors[i][2], 1.0)
    lut.SetRange(table_range)
    return lut


def pv_rand_cmap(N=256, first_is_black=False):
    '''Write out the random color map in paraview xml format.

    This method print out the XML declaration of the random colormap.
    This may be saved to a text file and used in paraview.

    :param int N: The number of colors in the colormap.
    :param bool first_is_black: Force the first color to be black.
    '''
    np.random.seed(13)
    rand_colors = np.random.rand(N, 3)
    if first_is_black:
        rand_colors[0] = [0., 0., 0.]  # enforce black background
    print('<ColorMap name="random" space="RGB">')
    for i in range(N):
        print('<Point x="%d" o="1" r="%8.6f" g="%8.6f" b="%8.6f"/>' %
              (i, rand_colors[i][0], rand_colors[i][1], rand_colors[i][2]))
    print('</ColorMap>')


def gray_cmap(table_range=(0, 255)):
    '''create a black and white colormap.

    *Parameters*

    **table_range**: 2 values tuple (default: (0,255))
    start and end values for the table range.

    *Returns*

    A vtkLookupTable from black to white.
    '''
    lut = vtk.vtkLookupTable()
    lut.SetSaturationRange(0, 0)
    lut.SetHueRange(0, 0)
    lut.SetTableRange(table_range)
    lut.SetValueRange(0, 1)
    lut.SetRampToLinear()
    lut.Build()
    return lut


def invert_cmap(ref_lut):
    '''invert a VTK lookup table.

    *Parameters*

    **ref_lut**: The lookup table to invert.

    *Returns*

    A reverse vtkLookupTable.
    '''
    N = ref_lut.GetNumberOfTableValues()
    lut = vtk.vtkLookupTable()
    lut.SetNumberOfTableValues(N)
    lut.Build()
    for i in range(N):
        lut.SetTableValue(i, ref_lut.GetTableValue(N - i))
    lut.SetRange(ref_lut.GetTableRange())
    return lut


def hsv_cmap(N=64, table_range=(0, 255)):
    '''Create a VTK look up table similar to matlab's hsv.

    *Parameters*

    **N**: int, number of colors in the table.

    **table_range**: 2 values tuple (default: (0,255))
    start and end values for the table range.

    *Returns*

    A vtkLookupTable.
    '''
    lut = vtk.vtkLookupTable()
    lut.SetHueRange(0.0, 1.0)
    lut.SetSaturationRange(1.0, 1.0)
    lut.SetValueRange(1.0, 1.0)
    lut.SetNumberOfColors(N)
    lut.Build()
    lut.SetRange(table_range)
    return lut


def jet_cmap(N=64, table_range=(0, 255)):
    '''Create a VTK look up table similar to matlab's jet.

    *Parameters*

    **N**: int, number of colors in the table.

    **table_range**: 2 values tuple (default: (0,255))
    start and end values for the table range.

    *Returns*

    A vtkLookupTable from blue to red.
    '''
    lut = vtk.vtkLookupTable()
    lut.SetHueRange(0.667, 0.0)
    lut.SetNumberOfColors(N)
    lut.Build()
    lut.SetRange(table_range)
    return lut


def hot_cmap(table_range=(0, 255)):
    '''Create a VTK look up table similar to matlab's hot.

    *Parameters*

    **table_range**: 2 values tuple (default: (0,255))
    start and end values for the table range.

    *Returns*

    A vtkLookupTable from white to red.
    '''
    lut = vtk.vtkLookupTable()
    lutNum = 64
    lut.SetNumberOfTableValues(lutNum)
    lut.Build()
    lut.SetTableValue(0, 0.041667, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(1, 0.083333, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(2, 0.125000, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(3, 0.166667, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(4, 0.208333, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(5, 0.250000, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(6, 0.291667, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(7, 0.333333, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(8, 0.375000, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(9, 0.416667, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(10, 0.458333, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(11, 0.500000, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(12, 0.541667, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(13, 0.583333, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(14, 0.625000, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(15, 0.666667, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(16, 0.708333, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(17, 0.750000, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(18, 0.791667, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(19, 0.833333, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(20, 0.875000, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(21, 0.916667, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(22, 0.958333, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(23, 1.000000, 0.000000, 0.000000, 1.0)
    lut.SetTableValue(24, 1.000000, 0.041667, 0.000000, 1.0)
    lut.SetTableValue(25, 1.000000, 0.083333, 0.000000, 1.0)
    lut.SetTableValue(26, 1.000000, 0.125000, 0.000000, 1.0)
    lut.SetTableValue(27, 1.000000, 0.166667, 0.000000, 1.0)
    lut.SetTableValue(28, 1.000000, 0.208333, 0.000000, 1.0)
    lut.SetTableValue(29, 1.000000, 0.250000, 0.000000, 1.0)
    lut.SetTableValue(30, 1.000000, 0.291667, 0.000000, 1.0)
    lut.SetTableValue(31, 1.000000, 0.333333, 0.000000, 1.0)
    lut.SetTableValue(32, 1.000000, 0.375000, 0.000000, 1.0)
    lut.SetTableValue(33, 1.000000, 0.416667, 0.000000, 1.0)
    lut.SetTableValue(34, 1.000000, 0.458333, 0.000000, 1.0)
    lut.SetTableValue(35, 1.000000, 0.500000, 0.000000, 1.0)
    lut.SetTableValue(36, 1.000000, 0.541667, 0.000000, 1.0)
    lut.SetTableValue(37, 1.000000, 0.583333, 0.000000, 1.0)
    lut.SetTableValue(38, 1.000000, 0.625000, 0.000000, 1.0)
    lut.SetTableValue(39, 1.000000, 0.666667, 0.000000, 1.0)
    lut.SetTableValue(40, 1.000000, 0.708333, 0.000000, 1.0)
    lut.SetTableValue(41, 1.000000, 0.750000, 0.000000, 1.0)
    lut.SetTableValue(42, 1.000000, 0.791667, 0.000000, 1.0)
    lut.SetTableValue(43, 1.000000, 0.833333, 0.000000, 1.0)
    lut.SetTableValue(44, 1.000000, 0.875000, 0.000000, 1.0)
    lut.SetTableValue(45, 1.000000, 0.916667, 0.000000, 1.0)
    lut.SetTableValue(46, 1.000000, 0.958333, 0.000000, 1.0)
    lut.SetTableValue(47, 1.000000, 1.000000, 0.000000, 1.0)
    lut.SetTableValue(48, 1.000000, 1.000000, 0.062500, 1.0)
    lut.SetTableValue(49, 1.000000, 1.000000, 0.125000, 1.0)
    lut.SetTableValue(50, 1.000000, 1.000000, 0.187500, 1.0)
    lut.SetTableValue(51, 1.000000, 1.000000, 0.250000, 1.0)
    lut.SetTableValue(52, 1.000000, 1.000000, 0.312500, 1.0)
    lut.SetTableValue(53, 1.000000, 1.000000, 0.375000, 1.0)
    lut.SetTableValue(54, 1.000000, 1.000000, 0.437500, 1.0)
    lut.SetTableValue(55, 1.000000, 1.000000, 0.500000, 1.0)
    lut.SetTableValue(56, 1.000000, 1.000000, 0.562500, 1.0)
    lut.SetTableValue(57, 1.000000, 1.000000, 0.625000, 1.0)
    lut.SetTableValue(58, 1.000000, 1.000000, 0.687500, 1.0)
    lut.SetTableValue(59, 1.000000, 1.000000, 0.750000, 1.0)
    lut.SetTableValue(60, 1.000000, 1.000000, 0.812500, 1.0)
    lut.SetTableValue(61, 1.000000, 1.000000, 0.875000, 1.0)
    lut.SetTableValue(62, 1.000000, 1.000000, 0.937500, 1.0)
    lut.SetTableValue(63, 1.000000, 1.000000, 1.000000, 1.0)
    lut.SetRange(table_range)
    return lut


def add_hklplane_to_grain(hkl_plane, grid, orientation, origin=(0, 0, 0), opacity=1.0,
                          show_normal=False, normal_length=1.0, show_intersection=False, color_intersection=red):
    """Add a plane describing a crystal lattice plane to a VTK grid.

    :param hkl_plane: an instance of `HklPlane` describing the lattice plane.
    :param grid: a vtkunstructuredgrid instance representing the geometry of the grain.
    :param orientation: the grain orientation.
    :param origin: the origin of the plane in the grain.
    :param float opacity:
    :param show_normal: A flag to show the plane normal.
    :param normal_length: The length of the plane normal.
    :param show_intersection: A flag to show the intersection of the plane with the grain.
    :param tuple color_intersection: The color to display the intersection of the plane with the grain.
    :return: A VTK assembly with the grain, the plane, its normal and edge intersection if requested.
    """
    # compute the plane normal in the laboratory frame using the grain orientation
    gt = orientation.orientation_matrix().transpose()
    n_rot = np.dot(gt, hkl_plane.normal() / np.linalg.norm(hkl_plane.normal()))
    rot_plane = vtk.vtkPlane()
    rot_plane.SetOrigin(origin)
    # rotate the plane by setting the normal
    rot_plane.SetNormal(n_rot)
    return add_plane_to_grid(rot_plane, grid, opacity=opacity, show_normal=show_normal, normal_length=normal_length,
                             show_intersection=show_intersection, color_intersection=color_intersection)


def add_plane_to_grid(plane, grid, origin=None, color=None, opacity=0.3, show_normal=False, normal_length=1.0,
                      show_intersection=False, color_intersection=red):
    """Add a 3d plane inside another object.

    This function adds a plane inside another object described by a mesh (vtkunstructuredgrid). 
    The method is to use a vtkCutter with the mesh as input and the plane as the cut function. 
    The plane normal can be displayed and its length controlled.
    This may be used directly to add hkl planes inside a lattice cell or
    a grain.

    :param plane: A VTK implicit function describing the plane to add.
    :param grid: A VTK unstructured grid in which the plane is to be added.
    :param tuple origin: (x, y, z) tuple to define the origin of the plane.
    :param tuple color: A color defined by its rgb components.
    :param float opacity: Opacity value of the plane actor.
    :param bool show_normal: A flag to display the plane normal.
    :param normal_length: The length of the plane normal vector (1.0 by default).
    :param bool show_intersection: Also add the intersection of the plane with the grid in the assembly.
    :param tuple color_intersection: The color to display the intersection of the plane with the grid.
    :returns: A VTK assembly with the mesh, the plane, its normal and edge intersection if requested.
    """
    # prepare an assembly for the result
    assembly = vtk.vtkAssembly()
    # cut the unstructured grid with the plane
    planeCut = vtk.vtkCutter()
    if vtk.vtkVersion().GetVTKMajorVersion() > 5:
        planeCut.SetInputData(grid)
    else:
        planeCut.SetInput(grid)
    planeCut.SetCutFunction(plane)

    cutMapper = vtk.vtkPolyDataMapper()
    cutMapper.SetInputConnection(planeCut.GetOutputPort())
    cutActor = vtk.vtkActor()
    cutActor.SetMapper(cutMapper)
    if color:
        cutActor.GetProperty().SetColor(color)
    cutActor.GetProperty().SetOpacity(opacity)
    assembly.AddPart(cutActor)
    if show_normal:
        # add an arrow to display the normal to the plane
        if origin is None:
            origin = plane.GetOrigin()
        arrowActor = unit_arrow_3d(origin, normal_length * np.array(plane.GetNormal()), make_unit=False)
        assembly.AddPart(arrowActor)
    if show_intersection:
        extract = vtk.vtkFeatureEdges()
        extract.SetInputConnection(planeCut.GetOutputPort())
        edge_mapper = vtk.vtkPolyDataMapper()
        edge_mapper.SetInputConnection(extract.GetOutputPort())
        edge_mapper.SetScalarVisibility(0)
        edge_actor = vtk.vtkActor()
        edge_actor.SetMapper(edge_mapper)
        edge_actor.GetProperty().SetColor(color_intersection)
        edge_actor.GetProperty().SetLineWidth(3.0)
        assembly.AddPart(edge_actor)
    return assembly

def axes_actor(length=1.0, axisLabels=('x', 'y', 'z'), fontSize=20, color=None):
    '''Build an actor for the cartesian axes.

    :param length: The arrow length of the axes (1.0 by default).
    :type length: float or triple of float to specify the length of each axis individually.
    :param list axisLabels: Specify the axes labels (xyz by default), use axisLabels = None to hide the axis labels
    :param int fontSize: Font size for the axes labels (20 by default).
    :param tuple color: A single color defined by its rgb components (not set by default which keep the red, green, blue colors).
    :returns: A VTK assembly representing the cartesian axes.
    '''
    axes = vtk.vtkAxesActor()
    if isinstance(length, (float, int, np.int64, np.float64)):
        axes.SetTotalLength(length, length, length)
    else:
        assert(len(length) == 3)
        axes.SetTotalLength(length)
    axes.SetShaftTypeToCylinder()
    axes.SetCylinderRadius(0.02)
    if axisLabels:
        axes.SetXAxisLabelText(axisLabels[0])
        axes.SetYAxisLabelText(axisLabels[1])
        axes.SetZAxisLabelText(axisLabels[2])
        axprop = vtk.vtkTextProperty()
        axprop.SetColor(0, 0, 0)
        axprop.SetFontSize(fontSize)
        axprop.SetFontFamilyToArial()
        axes.GetXAxisCaptionActor2D().SetCaptionTextProperty(axprop)
        axes.GetYAxisCaptionActor2D().SetCaptionTextProperty(axprop)
        axes.GetZAxisCaptionActor2D().SetCaptionTextProperty(axprop)
    else:
        axes.SetAxisLabels(0)
    if color:
        # set the color of the whole triad
        collection = vtk.vtkPropCollection()
        axes.GetActors(collection)
        for i in range(collection.GetNumberOfItems()):
            collection.GetItemAsObject(i).GetProperty().SetColor(color)
    return axes


def grain_3d(grain, hklplanes=None, show_normal=False, plane_origins=None,
             plane_opacity=1.0, show_orientation=False, N=2048, verbose=False):
    """Creates a 3d representation of a crystallographic grain.

    This method creates a vtkActor object of the surface mesh
    representing a Grain object. An optional list of crystallographic
    planes can be given

    :param grain: the Grain object to be shown in 3d.
    :param list hklplanes: the list of HklPlanes object to add to the assembly.
    :param bool show_normal: show also the normal to the hkl planes if True.
    :param list plane_origins: the list of each plane origin (3d scene coordinates).
    :param float plane_opacity: set the opacity of the grain actor.
    :param bool show_orientation: show also the grain orientation with a vtkAxesActor placed at the grain center if True.
    :param int N: the number of colors to use in the colormap.
    :param bool verbose: activate verbose mode.
    :returns: a vtkAssembly of the grain mesh and the optional hkl planes.
    """
    assembly = vtk.vtkAssembly()
    # create mapper
    mapper = vtk.vtkDataSetMapper()
    if vtk.vtkVersion().GetVTKMajorVersion() > 5:
        mapper.SetInputData(grain.vtkmesh)
    else:
        mapper.SetInput(grain.vtkmesh)
    mapper.ScalarVisibilityOff()  # we use the grain id for chosing the color
    lut = rand_cmap(N, first_is_black=True, table_range=(0, N - 1))
    grain_actor = vtk.vtkActor()
    if verbose:
        print(grain.id)
        print(lut.GetTableValue(grain.id)[0:3])
    grain_actor.GetProperty().SetColor(lut.GetTableValue(grain.id)[0:3])
    grain_actor.GetProperty().SetOpacity(0.3)
    grain_actor.SetMapper(mapper)
    assembly.AddPart(grain_actor)
    if show_orientation:
        local_orientation = add_local_orientation_axes(grain.orientation, axes_length=30)
        # add local orientation to the grain actor
        assembly.AddPart(local_orientation)
    # add all hkl planes
    if hklplanes:
        from itertools import cycle
        # colors for the intersection
        it = cycle([red, green, blue, orange])
        # look at each hkl plane in the list
        for i, hklplane in enumerate(hklplanes):
            # the grain has its center of mass at the origin
            origin = [(0., 0., 0.)]
            color = next(it)
            if plane_origins is not None:
                origin = plane_origins[i]  # in unit of the 3d scene
                if verbose:
                    print('using origin %s' % str(origin))
                if type(origin) is not list:
                    origin = [plane_origins[i]]
            # we will add a series of this plane (one per origin value)
            for o in origin:
                hklplaneActor = add_hklplane_to_grain(hklplane, grain.vtkmesh,
                                                      grain.orientation, origin=o, opacity=plane_opacity,
                                                      show_normal=show_normal, normal_length=50.,
                                                      show_intersection=True, color_intersection=color)
                assembly.AddPart(hklplaneActor)
            color = next(it)
    return assembly

# deprecated, will be removed soon
def add_grain_to_3d_scene(grain, hklplanes, show_orientation=False):
    assembly = vtk.vtkAssembly()
    # create mapper
    print('creating grain actor')
    mapper = vtk.vtkDataSetMapper()
    if vtk.vtkVersion().GetVTKMajorVersion() > 5:
        mapper.SetInputData(grain.vtkmesh)
    else:
        mapper.SetInput(grain.vtkmesh)
    mapper.ScalarVisibilityOff()  # we use the grain id for choosing the color
    lut = rand_cmap(N=2048, first_is_black=True, table_range=(0, 2047))
    grain_actor = vtk.vtkActor()
    grain_actor.GetProperty().SetColor(lut.GetTableValue(grain.id)[0:3])
    grain_actor.SetMapper(mapper)
    assembly.AddPart(grain_actor)
    # add all hkl planes and local grain orientation actor
{'fraction_non_alphanumeric': 0.11124190700412007, 'fraction_numerical': 0.10036716275680373, 'mean_word_length': 3.083781618404487, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3711710', 'n_tokens_mistral': 14858, 'n_tokens_neox': 12071, 'n_words': 3368}