title: Python Enthought Mayavi crashes on data update using mlab_source.reset
body: <p>I'm trying to update data in a mayavi 3D plot. Some of the changes to the data don't affect the data shape so the mlab_source.set() method can be used (which updates underlying data and refreshes the display without resetting the camera, regenerating the VTK pipeline, regenerating the underlying data structure, etc.  This is the best possible case for animation or quick plot updates.</p>
  
  <p>If the underlying data changes shape, the documentation recommends using the mlab_source.reset() method, which while not recreating the entire pipeline or messing up the current scene's camera, does cause the data structure to be rebuilt, costing some performance overhead. This is causing crashes for me.</p>
  
  <p>The worst way to go is completely deleting the plot source and generating a new one with a new call to mlab.mesh() or whatever function was used to plot the data.  This recreates a new VTK pipeline, new data structure, and resets the scene's view (loses current zoom and camera settings, which can make smooth interactivity impossible depending on the application).</p>
  
  <p>I've illustrated a simple example from my application in which a Sphere class can have it's properties manipulated (position, size, and resolution).  While changing position and size cause the coordinates to refresh, the data remains the same size. However changing the resolution affects the number of latitude and longitude subdivisions used to represent the sphere, which changes the number of coordinates.  When attempting to use the "reset" function, the interpreter crashes completely.  I'm pretty sure this is a C level segfault in the VTK code based on similar errors around the web. <a href="http://web.archiveorange.com/archive/v/alwBjVBzsQshj8DbZfqZ" rel="nofollow">This thread</a> seems to indicate the core developers dealing with the problem almost 5 years ago, but I can't tell if it was truly solved.</p>
  
  <p>I am on Mayavi 4.3.1 which I got along with the Enthought Tool Suite with Python(x, y).  I'm on Windows 7 64-bit with Python 2.7.5.  I'm using PySide, but I removed those calls and let mlab work by itself for this example.</p>
  
  <p>Here's my example that shows mlab_source.set() working but crashes on mlab_source.reset().  Any thoughts on why it's crashing?  Can others duplicate it?  I'm pretty sure there are other ways to update the data through the source (TVTK?) object, but I can't find it in the docs and the dozens of traits related attributes are very difficult to wade through.</p>
  
  <p>Any help is appreciated!</p>
  
  <pre><code>#Numpy Imports
  from time import sleep
  import numpy as np
  from numpy import sin, cos, pi
  
  
  class Sphere(object):
      """
      Class for a sphere
      """
      def __init__(self, c=None, r=None, n=None):
  
          #Initial defaults
          self._coordinates = None
          self._c = np.array([0.0, 0.0, 0.0])
          self._r = 1.0
          self._n = 20
          self._hash = []
          self._required_inputs = [('c', list),
                                   ('r', float)]
  
          #Assign Inputs
          if c is not None:
              self.c = c
          else:
              self.c = self._c
  
          if r is not None:
              self.r = r
          else:
              self.r = self._r
  
  
          if n is not None:
              self.n = n
          else:
              self.n = self._n
  
      @property
      def c(self):
          """
          Center point of sphere
           - Point is specified as a cartesian coordinate triplet, [x, y, z]
           - Coordinates are stored as a numpy array
           - Coordinates input as a list will be coerced to a numpy array
          """
          return self._c
  
      @c.setter
      def c(self, val):
          if isinstance(val, list):
              val = np.array(val)
  
          self._c = val
  
      @property
      def r(self):
          """
          Radius of sphere
          """
          return self._r
  
      @r.setter
      def r(self, val):
          if val &lt; 0:
              raise ValueError("Sphere radius input must be positive")
          self._r = val
  
      @property
      def n(self):
          """
          Resolution of curvature
           - Number of points used to represent circles and arcs
           - For a sphere, n is the number of subdivisions per hemisphere (in both latitude and longitude)
          """
          return self._n
  
      @n.setter
      def n(self, val):
          if val &lt; 0:
              raise ValueError("Sphere n-value for specifying arc/circle resolution must be positive")
          self._n = val
  
      @property
      def coordinates(self):
          """
          Returns x, y, z coordinate arrays to visualize the shape in 3D
          """
          self._lazy_update()
          return self._coordinates
  
      def _lazy_update(self):
          """
          Only update the coordinates data if necessary
          """
          #Get a newly calculated hash based on the sphere's inputs
          new_hash = self._get_hash()
          #Get the old hash
          old_hash = self._hash
          #Check if the sphere's state has changed
          if new_hash != old_hash:
              #Something changed - update the coordinates
              self._update_coordinates()
  
      def _get_hash(self):
          """
          Get the sphere's inputs as an immutable data structure
          """
          return tuple(map(tuple, [self._c, [self._r, self._n]]))
  
      def _update_coordinates(self):
          """
          Calculate 3D coordinates to represent the sphere
          """
  
          c, r, n = self._c, self._r, self._n
  
          #Get the angular distance between latitude and longitude lines
          dphi, dtheta = pi / n, pi / n
  
          #Generate a latitude and longitude grid
          [phi, theta] = np.mgrid[0:pi + dphi*1.0:dphi,
                                  0:2 * pi + dtheta*1.0:dtheta]
  
          #Map the latitude longitude grid into cartesian x, y, z coordinates
          x = c[0] + r * cos(phi) * sin(theta)
          y = c[1] + r * sin(phi) * sin(theta)
          z = c[2] + r * cos(theta)
  
          #Store the coordinates
          self._coordinates = x, y, z
          #Update the hash to coordinates to these coordinates
          self._hash = self._get_hash()
  
  
  if __name__ == '__main__':
  
      from mayavi import mlab
  
      #Make a sphere
      sphere = Sphere()
  
      #Plot the sphere
      source = mlab.mesh(*sphere.coordinates, representation='wireframe')
  
      #Get the mlab_source
      ms = source.mlab_source
  
      #Increase the sphere's radius by 2
      sphere.r *= 2
  
      #New coordinates (with larger radius)
      x, y, z = sphere.coordinates
      #Currently plotted coordinates
      x_old, y_old, z_old = ms.x, ms.y, ms.z
  
      #Verify that new x, y, z  are all same shape as old x, y, z
      data_is_same_shape = all([i.shape == j.shape for i, j in zip([x_old, y_old, z_old], [x, y, z])])
  
      #Pause to see the old sphere
      sleep(2)
  
      #Check if data has changed shape... (shouldn't have)
      if data_is_same_shape:
          print "Updating same-shaped data"
          ms.set(x=x, y=y, z=z)
      else:
          print "Updating with different shaped data"
          ms.reset(x=x, y=y, z=z)
  
      #Increase the arc resolution
      sphere.n = 50
  
      #New coordinates (with more points)
      x, y, z = sphere.coordinates
      #Currently plotted coordinates
      x_old, y_old, z_old = ms.x, ms.y, ms.z
  
      #Verify that new x, y, z  are all same shape as old x, y, z
      data_is_same_shape = all([i.shape == j.shape for i, j in zip([x_old, y_old, z_old], [x, y, z])])
  
      #Pause to see the bigger sphere
      sleep(2)
  
      #Check if data has changed shape... (should have this time...)
      if data_is_same_shape:
          print "Updating same-shaped data"
          ms.set(x=x, y=y, z=z)
      else:
          #This is where the segfault / crash occurs
          print "Updating with different shaped data"
          ms.reset(x=x, y=y, z=z)
  
      mlab.show()
  </code></pre>
  
  <p>EDIT:</p>
  
  <p>I just verified that all of <a href="https://github.com/enthought/mayavi/blob/1eb4f97647e1b0f0535521020d3953957b01fcaa/mayavi/tests/test_mlab_source.py#L627" rel="nofollow">these mlab_source tests</a> pass for me which includes testing reset on an MGridSource.  This does show some possible workarounds like accessing <code>source.mlab_source.dataset.points</code> ... maybe there's a way to update the data manually?</p>
  
  <p>EDIT 2:</p>
  
  <p>I tried this:</p>
  
  <pre><code>p = np.array([x.flatten(), y.flatten(), z.flatten()]).T
  ms.dataset.points = p
  ms.dataset.point_data.scalars = np.zeros(x.shape)
  ms.dataset.points.modified()
  
  #Regenerate the data structure
  ms.reset(x=x, y=y, z=z)
  </code></pre>
  
  <p>It appears that modifying the TVTK Polydata object directly partly works.  It appears that it's updating the points without also auto-fixing the connectivity, which is why I have to also run the mlab_source.reset().  I assume the reset() can work now because the data coming in has the same number of points and the mlab_source handles auto-generating the connectivity data. It still crashes when reducing the number of points, maybe because connectivity data exists for points that don't exist?  I'm still very frustrated with this.</p>
  
  <p>EDIT 3:</p>
  
  <p>I've implemented the brute force method of just generating a new surface from mlab.mesh().  To prevent resetting the view I disable rendering and store the camera settings, then restore the camera settings after mlab.mesh() and then re-enable rendering.  Seems to work quick enough - still wish underlying data could be updated with reset()</p>
  
  <p>Here's the entire class I use to manage plotting objects (responds to GUI signals after an edit has been made).</p>
  
  <pre><code>class PlottablePrimitive(QtCore.QObject):
  
      def __init__(self, parent=None, shape=None, scene=None, mlab=None):
          super(PlottablePrimitive, self).__init__(parent=parent)
  
          self._shape = None
          self._scene = None
          self._mlab = None
          self._source = None
          self._color = [0.706, 0.510, 0.196]
          self._visible = True
          self._opacity = 1.0
          self._camera = {'position': None,
                          'focal_point': None,
                          'view_angle': None,
                          'view_up': None,
                          'clipping_range': None}
  
          if shape is not None:
              self._shape = shape
  
          if scene is not None:
              self._scene = scene
  
          if mlab is not None:
              self._mlab = mlab
  
      @property
      def shape(self):
          return self._shape
  
      @shape.setter
      def shape(self, val):
          self._shape = val
  
      @property
      def color(self):
          return self._color
  
      @color.setter
      def color(self, color):
          self._color = color
          if self._source is not None:
              surface = self._source.children[0].children[0].children[0]
              surface.actor.mapper.scalar_visibility = False
              surface.actor.property.color = tuple(color)
  
      def plot(self):
          x, y, z = self._shape.coordinates
          self._source = self._mlab.mesh(x, y, z)
  
      def update_plot(self):
          ms = self._source.mlab_source
          x, y, z = self._shape.coordinates
          a, b, c = ms.x, ms.y, ms.z
          data_is_same_shape = all([i.shape == j.shape for i, j in zip([a, b, c], [x, y, z])])
  
          if data_is_same_shape:
              print "Same Data Shape... updating"
              #Update the data in-place
              ms.set(x=x, y=y, z=z)
          else:
              print "New Data Shape... resetting data"
  
              method = 'new_source'
  
              if method == 'tvtk':
                  #Modify TVTK directly
                  p = np.array([x.flatten(), y.flatten(), z.flatten()]).T
                  ms.dataset.points = p
                  ms.dataset.point_data.scalars = np.zeros(x.shape)
                  ms.dataset.points.modified()
  
                  #Regenerate the data structure
                  ms.reset(x=x, y=y, z=z)
              elif method == 'reset':
                  #Regenerate the data structure
                  ms.reset(x=x, y=y, z=z)
              elif method == 'new_source':
                  scene = self._scene
  
                  #Save camera settings
                  self._save_camera()
  
                  #Disable rendering
                  self._scene.disable_render = True
  
                  #Delete old plot
                  self.delete_plot()
  
                  #Generate new mesh
                  self._source = self._mlab.mesh(x, y, z)
  
                  #Reset camera
                  self._restore_camera()
                  self._scene.disable_render = False
  
      def _save_camera(self):
          scene = self._scene
  
          #Save camera settings
          for setting in self._camera.keys():
              self._camera[setting] = getattr(scene.camera, setting)
  
      def _restore_camera(self):
          scene = self._scene
  
          #Save camera settings
          for setting in self._camera.keys():
              if self._camera[setting] is not None:
                  setattr(scene.camera, setting, self._camera[setting])
  
      def delete_plot(self):
          #Remove
          if self._source is not None:
              self._source.remove()
              self._source = None
  </code></pre>