{"action":"created","comment":{"url":"https://api.github.com/repos/nipy/dipy/pulls/comments/38283112","id":38283112,"diff_hunk":"@@ -0,0 +1,648 @@\n+# from __future__ import division, print_function, absolute_import\n+\n+import numpy as np\n+\n+from dipy.viz.colormap import line_colors\n+from dipy.viz.utils import numpy_to_vtk_points, numpy_to_vtk_colors\n+from dipy.viz.utils import set_input, map_coordinates_3d_4d\n+from dipy.core.ndindex import ndindex\n+\n+# Conditional import machinery for vtk\n+from dipy.utils.optpkg import optional_package\n+\n+# Allow import, but disable doctests if we don't have vtk\n+vtk, have_vtk, setup_module = optional_package('vtk')\n+colors, have_vtk_colors, _ = optional_package('vtk.util.colors')\n+numpy_support, have_ns, _ = optional_package('vtk.util.numpy_support')\n+\n+if have_vtk:\n+\n+    version = vtk.vtkVersion.GetVTKSourceVersion().split(' ')[-1]\n+    major_version = vtk.vtkVersion.GetVTKMajorVersion()\n+\n+\n+def slicer(data, affine=None, value_range=None, opacity=1.,\n+          lookup_colormap=None):\n+    \"\"\" Cuts 3D scalar or rgb volumes into images\n+\n+    Parameters\n+    ----------\n+    data : array, shape (X, Y, Z) or (X, Y, Z, 3)\n+        A grayscale or rgb 4D volume as a numpy array.\n+    affine : array, shape (3, 3)\n+        Grid to space (usually RAS 1mm) transformation matrix\n+    value_range : None or tuple (2,)\n+        If None then the values will be interpolated to (0, 255) from\n+        (min, max). Otherwise from (value_range[0], value_range[1]).\n+    opacity : float\n+        Opacity of 0 means completely transparent and 1 completely visible.\n+    lookup_colormap : vtkLookupTable\n+        If None (default) then a grayscale map is created.\n+\n+    Returns\n+    -------\n+    image_actor : ImageActor\n+        An object that is capable of displaying different parts of the volume\n+        as slices. The key method of this object is ``display_extent`` where\n+        one can input grid coordinates and display the slice in space (or grid)\n+        coordinates as calculated by the affine parameter.\n+\n+    \"\"\"\n+    if data.ndim != 3:\n+        if data.ndim == 4:\n+            if data.shape[3] != 3:\n+                raise ValueError('Only RGB 3D arrays are currently supported.')\n+            else:\n+                nb_components = 3\n+        else:\n+            raise ValueError('Only 3D arrays are currently supported.')\n+    else:\n+        nb_components = 1\n+\n+    if value_range is None:\n+        vol = np.interp(data, xp=[data.min(), data.max()], fp=[0, 255])\n+    else:\n+        vol = np.interp(data, xp=[value_range[0], value_range[1]], fp=[0, 255])\n+    vol = vol.astype('uint8')\n+\n+    im = vtk.vtkImageData()\n+    if major_version <= 5:\n+        im.SetScalarTypeToUnsignedChar()\n+    I, J, K = vol.shape[:3]\n+    im.SetDimensions(I, J, K)\n+    voxsz = (1., 1., 1.)\n+    # im.SetOrigin(0,0,0)\n+    im.SetSpacing(voxsz[2], voxsz[0], voxsz[1])\n+    if major_version <= 5:\n+        im.AllocateScalars()\n+        im.SetNumberOfScalarComponents(nb_components)\n+    else:\n+        im.AllocateScalars(vtk.VTK_UNSIGNED_CHAR, nb_components)\n+\n+    # copy data\n+    # what I do below is the same as what is commented here but much faster\n+    # for index in ndindex(vol.shape):\n+    #     i, j, k = index\n+    #     im.SetScalarComponentFromFloat(i, j, k, 0, vol[i, j, k])\n+    vol = np.swapaxes(vol, 0, 2)\n+    vol = np.ascontiguousarray(vol)\n+\n+    if nb_components == 1:\n+        vol = vol.ravel()\n+    else:\n+        vol = np.reshape(vol, [np.prod(vol.shape[:3]), vol.shape[3]])\n+\n+    uchar_array = numpy_support.numpy_to_vtk(vol, deep=0)\n+    im.GetPointData().SetScalars(uchar_array)\n+\n+    if affine is None:\n+        affine = np.eye(4)\n+\n+    # Set the transform (identity if none given)\n+    transform = vtk.vtkTransform()\n+    transform_matrix = vtk.vtkMatrix4x4()\n+    transform_matrix.DeepCopy((\n+        affine[0][0], affine[0][1], affine[0][2], affine[0][3],\n+        affine[1][0], affine[1][1], affine[1][2], affine[1][3],\n+        affine[2][0], affine[2][1], affine[2][2], affine[2][3],\n+        affine[3][0], affine[3][1], affine[3][2], affine[3][3]))\n+    transform.SetMatrix(transform_matrix)\n+    transform.Inverse()\n+\n+    # Set the reslicing\n+    image_resliced = vtk.vtkImageReslice()\n+    set_input(image_resliced, im)\n+    image_resliced.SetResliceTransform(transform)\n+    image_resliced.AutoCropOutputOn()\n+    image_resliced.SetInterpolationModeToLinear()\n+    image_resliced.Update()\n+\n+    if nb_components == 1:\n+        if lookup_colormap is None:\n+            # Create a black/white lookup table.\n+            lut = colormap_lookup_table((0, 255), (0, 0), (0, 0), (0, 1))\n+        else:\n+            lut = lookup_colormap\n+\n+    x1, x2, y1, y2, z1, z2 = im.GetExtent()\n+    ex1, ex2, ey1, ey2, ez1, ez2 = image_resliced.GetOutput().GetExtent()\n+\n+    class ImageActor(vtk.vtkImageActor):\n+\n+        def input_connection(self, output):\n+            if vtk.VTK_MAJOR_VERSION <= 5:\n+                self.SetInput(output.GetOutput())\n+            else:\n+                self.GetMapper().SetInputConnection(output.GetOutputPort())\n+            self.output = output\n+            self.shape = (ex2 + 1, ey2 + 1, ez2 + 1)\n+\n+        def display_extent(self, x1, x2, y1, y2, z1, z2):\n+            self.SetDisplayExtent(x1, x2, y1, y2, z1, z2)\n+            if vtk.VTK_MAJOR_VERSION > 5:\n+                self.Update()\n+\n+        def display(self, x=None, y=None, z=None):\n+            if x is None and y is None and z is None:\n+                self.display_extent(ex1, ex2, ey1, ey2, ez2/2, ez2/2)\n+            if x is not None:\n+                self.display_extent(x, x, ey1, ey2, ez1, ez2)\n+            if y is not None:\n+                self.display_extent(ex1, ex2, y, y, ez1, ez2)\n+            if z is not None:\n+                self.display_extent(ex1, ex2, ey1, ey2, z, z)\n+\n+        def opacity(self, value):\n+            if vtk.VTK_MAJOR_VERSION <= 5:\n+                self.SetOpacity(value)\n+            else:\n+                self.GetProperty().SetOpacity(value)\n+\n+        def copy(self):\n+            im_actor = ImageActor()\n+            im_actor.input_connection(self.output)\n+            im_actor.SetDisplayExtent(*self.GetDisplayExtent())\n+            im_actor.opacity(opacity)\n+            return im_actor\n+\n+        def set_position(self, wx, wy, wz):\n+            self.SetPosition(wx, wy, wz)\n+\n+        def get_position(self):\n+            return self.GetPosition()\n+\n+    image_actor = ImageActor()\n+    if nb_components == 1:\n+        plane_colors = vtk.vtkImageMapToColors()\n+        plane_colors.SetLookupTable(lut)\n+        plane_colors.SetInputConnection(image_resliced.GetOutputPort())\n+        plane_colors.Update()\n+        image_actor.input_connection(plane_colors)\n+    else:\n+        image_actor.input_connection(image_resliced)\n+    image_actor.display()\n+    image_actor.opacity(opacity)\n+\n+    return image_actor\n+\n+\n+def streamtube(lines, colors=None, opacity=1, linewidth=0.01, tube_sides=9,\n+               lod=True, lod_points=10 ** 4, lod_points_size=3,\n+               spline_subdiv=None, lookup_colormap=None):\n+    \"\"\" Uses streamtubes to visualize polylines\n+\n+    Parameters\n+    ----------\n+    lines : list\n+        list of N curves represented as 2D ndarrays\n+\n+    colors : array (N, 3), list of arrays, tuple (3,), array (K,), None\n+        If None then a standard orientation colormap is used for every line.\n+        If one tuple of color is used. Then all streamlines will have the same\n+        colour.\n+        If an array (N, 3) is given, where N is equal to the number of lines.\n+        Then every line is coloured with a different RGB color.\n+        If a list of RGB arrays is given then every point of every line takes\n+        a different color.\n+        If an array (K, ) is given, where K is the number of points of all\n+        lines then these are considered as the values to be used by the\n+        colormap.\n+        If an array (L, ) is given, where L is the number of streamlines then\n+        these are considered as the values to be used by the colormap per\n+        streamline.\n+        If an array (X, Y, Z) or (X, Y, Z, 3) is given then the values for the\n+        colormap are interpolated automatically using trilinear interpolation.\n+\n+    opacity : float\n+    linewidth : float\n+    tube_sides : int\n+    lod : bool\n+        use vtkLODActor rather than vtkActor\n+    lod_points : int\n+        number of points to be used when LOD is in effect\n+    lod_points_size : int\n+        size of points when lod is in effect\n+    spline_subdiv : int\n+        number of splines subdivision to smooth streamtubes\n+    lookup_colormap : bool\n+        add a default lookup table to the colormap\n+\n+    Examples\n+    --------\n+    >>> from dipy.viz import fvtk\n+    >>> r=fvtk.ren()\n+    >>> lines=[np.random.rand(10, 3), np.random.rand(20, 3)]\n+    >>> colors=np.random.rand(2, 3)\n+    >>> c=fvtk.streamtube(lines, colors)\n+    >>> fvtk.add(r,c)\n+    >>> #fvtk.show(r)\n+\n+    Notes\n+    -----\n+    Streamtubes can be heavy on GPU when loading many streamlines and\n+    therefore, you may experience slow rendering time depending on system GPU.\n+    A solution to this problem is to reduce the number of points in each\n+    streamline. In Dipy we provide an algorithm that will reduce the number of\n+    points on the straighter parts of the streamline but keep more points on\n+    the curvier parts. This can be used in the following way\n+\n+    from dipy.tracking.distances import approx_polygon_track\n+    lines = [approx_polygon_track(line, 0.2) for line in lines]\n+\n+    Alternatively we suggest using the ``line`` actor which is much more\n+    efficient.\n+\n+    See Also\n+    --------\n+    dipy.viz.actor.line\n+    \"\"\"\n+    # Poly data with lines and colors\n+    poly_data, is_colormap = lines_to_vtk_polydata(lines, colors)\n+    next_input = poly_data\n+\n+    # Set Normals\n+    poly_normals = set_input(vtk.vtkPolyDataNormals(), next_input)\n+    poly_normals.ComputeCellNormalsOn()\n+    poly_normals.ComputePointNormalsOn()\n+    poly_normals.ConsistencyOn()\n+    poly_normals.AutoOrientNormalsOn()\n+    poly_normals.Update()\n+    next_input = poly_normals.GetOutputPort()\n+\n+    # Spline interpolation\n+    if (spline_subdiv is not None) and (spline_subdiv > 0):\n+        spline_filter = set_input(vtk.vtkSplineFilter(), next_input)\n+        spline_filter.SetSubdivideToSpecified()\n+        spline_filter.SetNumberOfSubdivisions(spline_subdiv)\n+        spline_filter.Update()\n+        next_input = spline_filter.GetOutputPort()\n+\n+    # Add thickness to the resulting lines\n+    tube_filter = set_input(vtk.vtkTubeFilter(), next_input)\n+    tube_filter.SetNumberOfSides(tube_sides)\n+    tube_filter.SetRadius(linewidth)\n+    # tube_filter.SetVaryRadiusToVaryRadiusByScalar()\n+    tube_filter.CappingOn()\n+    tube_filter.Update()\n+    next_input = tube_filter.GetOutputPort()\n+\n+    # Poly mapper\n+    poly_mapper = set_input(vtk.vtkPolyDataMapper(), next_input)\n+    poly_mapper.ScalarVisibilityOn()\n+    poly_mapper.SetScalarModeToUsePointFieldData()\n+    poly_mapper.SelectColorArray(\"Colors\")\n+    poly_mapper.GlobalImmediateModeRenderingOn()\n+    # poly_mapper.SetColorModeToMapScalars()\n+    poly_mapper.Update()\n+\n+    # Color Scale with a lookup table\n+    if is_colormap:\n+        if lookup_colormap is None:\n+            lookup_colormap = colormap_lookup_table()\n+        poly_mapper.SetLookupTable(lookup_colormap)\n+        poly_mapper.UseLookupTableScalarRangeOn()\n+        poly_mapper.Update()\n+\n+    # Set Actor\n+    if lod:\n+        actor = vtk.vtkLODActor()\n+        actor.SetNumberOfCloudPoints(lod_points)\n+        actor.GetProperty().SetPointSize(lod_points_size)\n+    else:\n+        actor = vtk.vtkActor()\n+\n+    actor.SetMapper(poly_mapper)\n+    actor.GetProperty().SetAmbient(0.1)  # .3\n+    actor.GetProperty().SetDiffuse(0.15)  # .3\n+    actor.GetProperty().SetSpecular(0.05)  # .3\n+    actor.GetProperty().SetSpecularPower(6)\n+    # actor.GetProperty().SetInterpolationToGouraud()\n+    actor.GetProperty().SetInterpolationToPhong()\n+    actor.GetProperty().BackfaceCullingOn()\n+    actor.GetProperty().SetOpacity(opacity)\n+\n+    return actor\n+\n+\n+def line(lines, colors=None, opacity=1, linewidth=1,\n+         spline_subdiv=None, lod=True, lod_points=10 ** 4, lod_points_size=3,\n+         lookup_colormap=None):\n+    \"\"\" Create an actor for one or more lines.\n+\n+    Parameters\n+    ------------\n+    lines :  list of arrays\n+\n+    colors : array (N, 3), list of arrays, tuple (3,), array (K,), None\n+        If None then a standard orientation colormap is used for every line.\n+        If one tuple of color is used. Then all streamlines will have the same\n+        colour.\n+        If an array (N, 3) is given, where N is equal to the number of lines.\n+        Then every line is coloured with a different RGB color.\n+        If a list of RGB arrays is given then every point of every line takes\n+        a different color.\n+        If an array (K, ) is given, where K is the number of points of all\n+        lines then these are considered as the values to be used by the\n+        colormap.\n+        If an array (L, ) is given, where L is the number of streamlines then\n+        these are considered as the values to be used by the colormap per\n+        streamline.\n+        If an array (X, Y, Z) or (X, Y, Z, 3) is given then the values for the\n+        colormap are interpolated automatically using trilinear interpolation.\n+\n+    opacity : float, optional\n+\n+    linewidth : float, optional\n+        Line thickness.\n+    spline_subdiv : int, optional\n+        number of splines subdivision to smooth streamtubes\n+    lookup_colormap : bool, optional\n+        add a default lookup table to the colormap\n+\n+    Returns\n+    ----------\n+    v : vtkActor object\n+        Line.\n+\n+    Examples\n+    ----------\n+    >>> from dipy.viz import fvtk\n+    >>> r=fvtk.ren()\n+    >>> lines=[np.random.rand(10,3), np.random.rand(20,3)]\n+    >>> colors=np.random.rand(2,3)\n+    >>> c=fvtk.line(lines, colors)\n+    >>> fvtk.add(r,c)\n+    >>> #fvtk.show(r)\n+    \"\"\"\n+    # Poly data with lines and colors\n+    poly_data, is_colormap = lines_to_vtk_polydata(lines, colors)\n+    next_input = poly_data\n+\n+    # use spline interpolation\n+    if (spline_subdiv is not None) and (spline_subdiv > 0):\n+        spline_filter = set_input(vtk.vtkSplineFilter(), next_input)\n+        spline_filter.SetSubdivideToSpecified()\n+        spline_filter.SetNumberOfSubdivisions(spline_subdiv)\n+        spline_filter.Update()\n+        next_input = spline_filter.GetOutputPort()\n+\n+    poly_mapper = set_input(vtk.vtkPolyDataMapper(), next_input)\n+    poly_mapper.ScalarVisibilityOn()\n+    poly_mapper.SetScalarModeToUsePointFieldData()\n+    poly_mapper.SelectColorArray(\"Colors\")\n+    # poly_mapper.SetColorModeToMapScalars()\n+    poly_mapper.Update()\n+\n+    # Color Scale with a lookup table\n+    if is_colormap:\n+\n+        if lookup_colormap is None:\n+            lookup_colormap = colormap_lookup_table()\n+\n+        poly_mapper.SetLookupTable(lookup_colormap)\n+        poly_mapper.UseLookupTableScalarRangeOn()\n+        poly_mapper.Update()\n+\n+    # Set Actor\n+    if lod:\n+        actor = vtk.vtkLODActor()\n+        actor.SetNumberOfCloudPoints(lod_points)\n+        actor.GetProperty().SetPointSize(lod_points_size)\n+    else:\n+        actor = vtk.vtkActor()\n+\n+    # actor = vtk.vtkActor()\n+    actor.SetMapper(poly_mapper)\n+    actor.GetProperty().SetLineWidth(linewidth)\n+    actor.GetProperty().SetOpacity(opacity)\n+\n+    return actor\n+\n+\n+def lines_to_vtk_polydata(lines, colors=None):\n+    \"\"\" Create a vtkPolyData with lines and colors\n+\n+    Parameters\n+    ----------\n+    lines : list\n+        list of N curves represented as 2D ndarrays\n+    colors : array (N, 3), list of arrays, tuple (3,), array (K,), None\n+        If None then a standard orientation colormap is used for every line.\n+        If one tuple of color is used. Then all streamlines will have the same\n+        colour.\n+        If an array (N, 3) is given, where N is equal to the number of lines.\n+        Then every line is coloured with a different RGB color.\n+        If a list of RGB arrays is given then every point of every line takes\n+        a different color.\n+        If an array (K, ) is given, where K is the number of points of all\n+        lines then these are considered as the values to be used by the\n+        colormap.\n+        If an array (L, ) is given, where L is the number of streamlines then\n+        these are considered as the values to be used by the colormap per\n+        streamline.\n+        If an array (X, Y, Z) or (X, Y, Z, 3) is given then the values for the\n+        colormap are interpolated automatically using trilinear interpolation.\n+\n+    Returns\n+    -------\n+    poly_data :  vtkPolyData\n+    is_colormap : bool, true if the input color array was a colormap\n+    \"\"\"\n+\n+    # Get the 3d points_array\n+    points_array = np.vstack(lines)\n+\n+    nb_lines = len(lines)\n+    nb_points = len(points_array)\n+\n+    lines_range = range(nb_lines)\n+\n+    # Get lines_array in vtk input format\n+    lines_array = []\n+    points_per_line = np.zeros([nb_lines], np.int64)\n+    current_position = 0\n+    for i in lines_range:\n+        current_len = len(lines[i])\n+        points_per_line[i] = current_len\n+\n+        end_position = current_position + current_len\n+        lines_array += [current_len]\n+        lines_array += range(current_position, end_position)\n+        current_position = end_position\n+\n+    lines_array = np.array(lines_array)\n+\n+    # Set Points to vtk array format\n+    vtk_points = numpy_to_vtk_points(points_array)\n+\n+    # Set Lines to vtk array format\n+    vtk_lines = vtk.vtkCellArray()\n+    vtk_lines.GetData().DeepCopy(numpy_support.numpy_to_vtk(lines_array))\n+    vtk_lines.SetNumberOfCells(len(lines))\n+\n+    is_colormap = False\n+    # Get colors_array (reformat to have colors for each points)\n+    #           - if/else tested and work in normal simple case\n+    if colors is None:  # set automatic rgb colors\n+        cols_arr = line_colors(lines)\n+        colors_mapper = np.repeat(lines_range, points_per_line, axis=0)\n+        vtk_colors = numpy_to_vtk_colors(255 * cols_arr[colors_mapper])\n+    else:\n+        cols_arr = np.asarray(colors)\n+        if cols_arr.dtype == np.object:  # colors is a list of colors\n+            vtk_colors = numpy_to_vtk_colors(255 * np.vstack(colors))\n+        else:\n+            if len(cols_arr) == nb_points:\n+                vtk_colors = numpy_support.numpy_to_vtk(cols_arr, deep=True)\n+                is_colormap = True\n+\n+            elif cols_arr.ndim == 1:\n+                if len(cols_arr) == nb_lines:  # values for every streamline\n+                    cols_arrx = []\n+                    for (i, value) in enumerate(colors):\n+                        cols_arrx += lines[i].shape[0]*[value]\n+                    cols_arrx = np.array(cols_arrx)\n+                    vtk_colors = numpy_support.numpy_to_vtk(cols_arrx,\n+                                                            deep=True)\n+                    is_colormap = True\n+                else:  # the same colors for all points\n+                    vtk_colors = numpy_to_vtk_colors(\n+                        np.tile(255 * cols_arr, (nb_points, 1)))\n+\n+            elif cols_arr.ndim == 2:  # map color to each line\n+                colors_mapper = np.repeat(lines_range, points_per_line, axis=0)\n+                vtk_colors = numpy_to_vtk_colors(255 * cols_arr[colors_mapper])\n+            else:  # colormap\n+                #  get colors for each vertex\n+                cols_arr = map_coordinates_3d_4d(cols_arr, points_array)\n+                vtk_colors = numpy_support.numpy_to_vtk(cols_arr, deep=True)\n+                is_colormap = True\n+\n+    vtk_colors.SetName(\"Colors\")\n+\n+    # Create the poly_data\n+    poly_data = vtk.vtkPolyData()\n+    poly_data.SetPoints(vtk_points)\n+    poly_data.SetLines(vtk_lines)\n+    poly_data.GetPointData().SetScalars(vtk_colors)\n+    return poly_data, is_colormap\n+\n+\n+def colormap_lookup_table(scale_range=(0, 1), hue_range=(0.8, 0),\n+                          saturation_range=(1, 1), value_range=(0.8, 0.8)):\n+    \"\"\" Lookup table for the colormap\n+\n+    Parameters\n+    ----------\n+    scale_range : tuple\n+        It can be anything e.g. (0, 1) or (0, 255). Usually it is the mininum\n+        and maximum value of your data.\n+    hue_range : tuple of floats\n+        HSV values (min 0 and max 1)\n+    saturation_range : tuple of floats\n+        HSV values (min 0 and max 1)\n+    value_range : tuple of floats\n+        HSV value (min 0 and max 1)\n+\n+    Returns\n+    -------\n+    lookup_table : vtkLookupTable\n+\n+    \"\"\"\n+    lookup_table = vtk.vtkLookupTable()\n+    lookup_table.SetRange(scale_range)\n+    lookup_table.SetTableRange(scale_range)\n+\n+    lookup_table.SetHueRange(hue_range)\n+    lookup_table.SetSaturationRange(saturation_range)\n+    lookup_table.SetValueRange(value_range)\n+\n+    lookup_table.Build()\n+    return lookup_table\n+\n+\n+def scalar_bar(lookup_table=None, title=\" \"):\n+    \"\"\" Default scalar bar actor for a given colormap\n+\n+    Parameters\n+    ----------\n+    lookup_table : vtkLookupTable or None\n+        If None then ``colormap_lookup_table`` is called with default options.","path":"dipy/viz/actor.py","position":570,"original_position":570,"commit_id":"4ade99a8674ae2dfd459651236fc76f47db8e88b","original_commit_id":"4ade99a8674ae2dfd459651236fc76f47db8e88b","user":{"login":"arokem","id":118582,"avatar_url":"https://avatars.githubusercontent.com/u/118582?v=3","gravatar_id":"","url":"https://api.github.com/users/arokem","html_url":"https://github.com/arokem","followers_url":"https://api.github.com/users/arokem/followers","following_url":"https://api.github.com/users/arokem/following{/other_user}","gists_url":"https://api.github.com/users/arokem/gists{/gist_id}","starred_url":"https://api.github.com/users/arokem/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/arokem/subscriptions","organizations_url":"https://api.github.com/users/arokem/orgs","repos_url":"https://api.github.com/users/arokem/repos","events_url":"https://api.github.com/users/arokem/events{/privacy}","received_events_url":"https://api.github.com/users/arokem/received_events","type":"User","site_admin":false},"body":"=> :func:`colormap_lookup_table`","created_at":"2015-08-31T03:56:37Z","updated_at":"2015-08-31T03:56:37Z","html_url":"https://github.com/nipy/dipy/pull/587#discussion_r38283112","pull_request_url":"https://api.github.com/repos/nipy/dipy/pulls/587","_links":{"self":{"href":"https://api.github.com/repos/nipy/dipy/pulls/comments/38283112"},"html":{"href":"https://github.com/nipy/dipy/pull/587#discussion_r38283112"},"pull_request":{"href":"https://api.github.com/repos/nipy/dipy/pulls/587"}}},"pull_request":{"url":"https://api.github.com/repos/nipy/dipy/pulls/587","id":30952564,"html_url":"https://github.com/nipy/dipy/pull/587","diff_url":"https://github.com/nipy/dipy/pull/587.diff","patch_url":"https://github.com/nipy/dipy/pull/587.patch","issue_url":"https://api.github.com/repos/nipy/dipy/issues/587","number":587,"state":"open","locked":false,"title":"Fvtk 2.0 PR1","user":{"login":"Garyfallidis","id":134276,"avatar_url":"https://avatars.githubusercontent.com/u/134276?v=3","gravatar_id":"","url":"https://api.github.com/users/Garyfallidis","html_url":"https://github.com/Garyfallidis","followers_url":"https://api.github.com/users/Garyfallidis/followers","following_url":"https://api.github.com/users/Garyfallidis/following{/other_user}","gists_url":"https://api.github.com/users/Garyfallidis/gists{/gist_id}","starred_url":"https://api.github.com/users/Garyfallidis/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Garyfallidis/subscriptions","organizations_url":"https://api.github.com/users/Garyfallidis/orgs","repos_url":"https://api.github.com/users/Garyfallidis/repos","events_url":"https://api.github.com/users/Garyfallidis/events{/privacy}","received_events_url":"https://api.github.com/users/Garyfallidis/received_events","type":"User","site_admin":false},"body":"This will make you happy @arokem and @stefanv! Moving forward with the visualization capabilities of DIPY!!! The PR is ready for a review.\r\n\r\nThis pull request introduces a new API for visualization.\r\n\r\nHere are the main characteristics:\r\na) Allows for testable screenshots and renderers. Which is crucial for quality assurance.\r\nb) It contains 3 main modules (window, actor and widget). These are replacing the current fvtk module but fvtk can be used too for backwards compatibility.\r\nc) In window we have everything related to windowing, rendering and saving screenshots.\r\nd) In actor you will find the actors for lines and streamtubes etc. Not everything has moved yet from fvtk.py so that the PR doesn't become too large. But all actors should move in the next PRs.\r\ne) In widget we have buttons and sliders which can be used directly from inside OpenGL/VTK. No new dependencies.\r\nd) You can save and open file dialogues using Tk which is ported together with Python. No new dependencies.\r\nf) Because they are a lot of new concepts and ideas in this PR I wrote 4 tutorials to help the reviewing process. \r\n \r\nLet me know what you think.","created_at":"2015-03-11T15:15:14Z","updated_at":"2015-08-31T03:56:37Z","closed_at":null,"merged_at":null,"merge_commit_sha":"d90c0253e52c36a70ddc54d4466cb01156166ecc","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/nipy/dipy/pulls/587/commits","review_comments_url":"https://api.github.com/repos/nipy/dipy/pulls/587/comments","review_comment_url":"https://api.github.com/repos/nipy/dipy/pulls/comments{/number}","comments_url":"https://api.github.com/repos/nipy/dipy/issues/587/comments","statuses_url":"https://api.github.com/repos/nipy/dipy/statuses/4ade99a8674ae2dfd459651236fc76f47db8e88b","head":{"label":"Garyfallidis:fvtk_2.0","ref":"fvtk_2.0","sha":"4ade99a8674ae2dfd459651236fc76f47db8e88b","user":{"login":"Garyfallidis","id":134276,"avatar_url":"https://avatars.githubusercontent.com/u/134276?v=3","gravatar_id":"","url":"https://api.github.com/users/Garyfallidis","html_url":"https://github.com/Garyfallidis","followers_url":"https://api.github.com/users/Garyfallidis/followers","following_url":"https://api.github.com/users/Garyfallidis/following{/other_user}","gists_url":"https://api.github.com/users/Garyfallidis/gists{/gist_id}","starred_url":"https://api.github.com/users/Garyfallidis/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Garyfallidis/subscriptions","organizations_url":"https://api.github.com/users/Garyfallidis/orgs","repos_url":"https://api.github.com/users/Garyfallidis/repos","events_url":"https://api.github.com/users/Garyfallidis/events{/privacy}","received_events_url":"https://api.github.com/users/Garyfallidis/received_events","type":"User","site_admin":false},"repo":{"id":2997322,"name":"dipy","full_name":"Garyfallidis/dipy","owner":{"login":"Garyfallidis","id":134276,"avatar_url":"https://avatars.githubusercontent.com/u/134276?v=3","gravatar_id":"","url":"https://api.github.com/users/Garyfallidis","html_url":"https://github.com/Garyfallidis","followers_url":"https://api.github.com/users/Garyfallidis/followers","following_url":"https://api.github.com/users/Garyfallidis/following{/other_user}","gists_url":"https://api.github.com/users/Garyfallidis/gists{/gist_id}","starred_url":"https://api.github.com/users/Garyfallidis/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Garyfallidis/subscriptions","organizations_url":"https://api.github.com/users/Garyfallidis/orgs","repos_url":"https://api.github.com/users/Garyfallidis/repos","events_url":"https://api.github.com/users/Garyfallidis/events{/privacy}","received_events_url":"https://api.github.com/users/Garyfallidis/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/Garyfallidis/dipy","description":"Diffusion MR Imaging in Python","fork":true,"url":"https://api.github.com/repos/Garyfallidis/dipy","forks_url":"https://api.github.com/repos/Garyfallidis/dipy/forks","keys_url":"https://api.github.com/repos/Garyfallidis/dipy/keys{/key_id}","collaborators_url":"https://api.github.com/repos/Garyfallidis/dipy/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/Garyfallidis/dipy/teams","hooks_url":"https://api.github.com/repos/Garyfallidis/dipy/hooks","issue_events_url":"https://api.github.com/repos/Garyfallidis/dipy/issues/events{/number}","events_url":"https://api.github.com/repos/Garyfallidis/dipy/events","assignees_url":"https://api.github.com/repos/Garyfallidis/dipy/assignees{/user}","branches_url":"https://api.github.com/repos/Garyfallidis/dipy/branches{/branch}","tags_url":"https://api.github.com/repos/Garyfallidis/dipy/tags","blobs_url":"https://api.github.com/repos/Garyfallidis/dipy/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/Garyfallidis/dipy/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/Garyfallidis/dipy/git/refs{/sha}","trees_url":"https://api.github.com/repos/Garyfallidis/dipy/git/trees{/sha}","statuses_url":"https://api.github.com/repos/Garyfallidis/dipy/statuses/{sha}","languages_url":"https://api.github.com/repos/Garyfallidis/dipy/languages","stargazers_url":"https://api.github.com/repos/Garyfallidis/dipy/stargazers","contributors_url":"https://api.github.com/repos/Garyfallidis/dipy/contributors","subscribers_url":"https://api.github.com/repos/Garyfallidis/dipy/subscribers","subscription_url":"https://api.github.com/repos/Garyfallidis/dipy/subscription","commits_url":"https://api.github.com/repos/Garyfallidis/dipy/commits{/sha}","git_commits_url":"https://api.github.com/repos/Garyfallidis/dipy/git/commits{/sha}","comments_url":"https://api.github.com/repos/Garyfallidis/dipy/comments{/number}","issue_comment_url":"https://api.github.com/repos/Garyfallidis/dipy/issues/comments{/number}","contents_url":"https://api.github.com/repos/Garyfallidis/dipy/contents/{+path}","compare_url":"https://api.github.com/repos/Garyfallidis/dipy/compare/{base}...{head}","merges_url":"https://api.github.com/repos/Garyfallidis/dipy/merges","archive_url":"https://api.github.com/repos/Garyfallidis/dipy/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/Garyfallidis/dipy/downloads","issues_url":"https://api.github.com/repos/Garyfallidis/dipy/issues{/number}","pulls_url":"https://api.github.com/repos/Garyfallidis/dipy/pulls{/number}","milestones_url":"https://api.github.com/repos/Garyfallidis/dipy/milestones{/number}","notifications_url":"https://api.github.com/repos/Garyfallidis/dipy/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/Garyfallidis/dipy/labels{/name}","releases_url":"https://api.github.com/repos/Garyfallidis/dipy/releases{/id}","created_at":"2011-12-16T20:04:06Z","updated_at":"2015-07-31T20:43:18Z","pushed_at":"2015-08-28T22:02:56Z","git_url":"git://github.com/Garyfallidis/dipy.git","ssh_url":"git@github.com:Garyfallidis/dipy.git","clone_url":"https://github.com/Garyfallidis/dipy.git","svn_url":"https://github.com/Garyfallidis/dipy","homepage":"http://dipy.org","size":19559,"stargazers_count":4,"watchers_count":4,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":5,"mirror_url":null,"open_issues_count":6,"forks":5,"open_issues":6,"watchers":4,"default_branch":"placeholder"}},"base":{"label":"nipy:master","ref":"master","sha":"57f7ec926f914d72f7f2f8feb8ccb51ab827895d","user":{"login":"nipy","id":233707,"avatar_url":"https://avatars.githubusercontent.com/u/233707?v=3","gravatar_id":"","url":"https://api.github.com/users/nipy","html_url":"https://github.com/nipy","followers_url":"https://api.github.com/users/nipy/followers","following_url":"https://api.github.com/users/nipy/following{/other_user}","gists_url":"https://api.github.com/users/nipy/gists{/gist_id}","starred_url":"https://api.github.com/users/nipy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nipy/subscriptions","organizations_url":"https://api.github.com/users/nipy/orgs","repos_url":"https://api.github.com/users/nipy/repos","events_url":"https://api.github.com/users/nipy/events{/privacy}","received_events_url":"https://api.github.com/users/nipy/received_events","type":"Organization","site_admin":false},"repo":{"id":505209,"name":"dipy","full_name":"nipy/dipy","owner":{"login":"nipy","id":233707,"avatar_url":"https://avatars.githubusercontent.com/u/233707?v=3","gravatar_id":"","url":"https://api.github.com/users/nipy","html_url":"https://github.com/nipy","followers_url":"https://api.github.com/users/nipy/followers","following_url":"https://api.github.com/users/nipy/following{/other_user}","gists_url":"https://api.github.com/users/nipy/gists{/gist_id}","starred_url":"https://api.github.com/users/nipy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nipy/subscriptions","organizations_url":"https://api.github.com/users/nipy/orgs","repos_url":"https://api.github.com/users/nipy/repos","events_url":"https://api.github.com/users/nipy/events{/privacy}","received_events_url":"https://api.github.com/users/nipy/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/nipy/dipy","description":"Diffusion MR Imaging in Python","fork":false,"url":"https://api.github.com/repos/nipy/dipy","forks_url":"https://api.github.com/repos/nipy/dipy/forks","keys_url":"https://api.github.com/repos/nipy/dipy/keys{/key_id}","collaborators_url":"https://api.github.com/repos/nipy/dipy/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/nipy/dipy/teams","hooks_url":"https://api.github.com/repos/nipy/dipy/hooks","issue_events_url":"https://api.github.com/repos/nipy/dipy/issues/events{/number}","events_url":"https://api.github.com/repos/nipy/dipy/events","assignees_url":"https://api.github.com/repos/nipy/dipy/assignees{/user}","branches_url":"https://api.github.com/repos/nipy/dipy/branches{/branch}","tags_url":"https://api.github.com/repos/nipy/dipy/tags","blobs_url":"https://api.github.com/repos/nipy/dipy/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/nipy/dipy/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/nipy/dipy/git/refs{/sha}","trees_url":"https://api.github.com/repos/nipy/dipy/git/trees{/sha}","statuses_url":"https://api.github.com/repos/nipy/dipy/statuses/{sha}","languages_url":"https://api.github.com/repos/nipy/dipy/languages","stargazers_url":"https://api.github.com/repos/nipy/dipy/stargazers","contributors_url":"https://api.github.com/repos/nipy/dipy/contributors","subscribers_url":"https://api.github.com/repos/nipy/dipy/subscribers","subscription_url":"https://api.github.com/repos/nipy/dipy/subscription","commits_url":"https://api.github.com/repos/nipy/dipy/commits{/sha}","git_commits_url":"https://api.github.com/repos/nipy/dipy/git/commits{/sha}","comments_url":"https://api.github.com/repos/nipy/dipy/comments{/number}","issue_comment_url":"https://api.github.com/repos/nipy/dipy/issues/comments{/number}","contents_url":"https://api.github.com/repos/nipy/dipy/contents/{+path}","compare_url":"https://api.github.com/repos/nipy/dipy/compare/{base}...{head}","merges_url":"https://api.github.com/repos/nipy/dipy/merges","archive_url":"https://api.github.com/repos/nipy/dipy/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/nipy/dipy/downloads","issues_url":"https://api.github.com/repos/nipy/dipy/issues{/number}","pulls_url":"https://api.github.com/repos/nipy/dipy/pulls{/number}","milestones_url":"https://api.github.com/repos/nipy/dipy/milestones{/number}","notifications_url":"https://api.github.com/repos/nipy/dipy/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/nipy/dipy/labels{/name}","releases_url":"https://api.github.com/repos/nipy/dipy/releases{/id}","created_at":"2010-02-06T11:43:08Z","updated_at":"2015-08-30T19:46:08Z","pushed_at":"2015-08-28T13:55:46Z","git_url":"git://github.com/nipy/dipy.git","ssh_url":"git@github.com:nipy/dipy.git","clone_url":"https://github.com/nipy/dipy.git","svn_url":"https://github.com/nipy/dipy","homepage":"http://dipy.org","size":30081,"stargazers_count":63,"watchers_count":63,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":95,"mirror_url":null,"open_issues_count":92,"forks":95,"open_issues":92,"watchers":63,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/nipy/dipy/pulls/587"},"html":{"href":"https://github.com/nipy/dipy/pull/587"},"issue":{"href":"https://api.github.com/repos/nipy/dipy/issues/587"},"comments":{"href":"https://api.github.com/repos/nipy/dipy/issues/587/comments"},"review_comments":{"href":"https://api.github.com/repos/nipy/dipy/pulls/587/comments"},"review_comment":{"href":"https://api.github.com/repos/nipy/dipy/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/nipy/dipy/pulls/587/commits"},"statuses":{"href":"https://api.github.com/repos/nipy/dipy/statuses/4ade99a8674ae2dfd459651236fc76f47db8e88b"}}}}