class Converter ( HasStrictTraits ): # Trait definitions: input_amount = CFloat( 12.0, desc = "the input quantity" ) input_units = Units( 'inches', desc = "the input quantity's units" ) output_amount = Property( depends_on = [ 'input_amount', 'input_units', 'output_units' ], desc = "the output quantity" ) output_units = Units( 'feet', desc = "the output quantity's units" ) # User interface views: traits_view = View( VGroup( HGroup( Item( 'input_amount', springy = True ), Item( 'input_units', show_label = False ), label = 'Input', show_border = True ), HGroup( Item( 'output_amount', style = 'readonly', springy = True ), Item( 'output_units', show_label = False ), label = 'Output', show_border = True ), help = ViewHelp ), title = 'Units Converter', buttons = [ 'Undo', 'OK', 'Help' ] ) # Property implementations def _get_output_amount ( self ): return ((self.input_amount * self.input_units_) / self.output_units_)
class WarpScalarFactory(PipeFactory): """Applies the WarpScalar mayavi filter to the given VTK object.""" _target = Instance(filters.WarpScalar, ()) warp_scale = CFloat(1.0, adapts="filter.scale_factor", help="scale of the warp scalar")
class Title(SingletonModuleFactory): """Creates a title for the figure. **Function signature**:: title(text, ...) """ size = CFloat(1, help="the size of the title") height = CFloat(0.8, adapts='y_position', help="""height of the title, in portion of the figure height""") def _size_changed(self): self._target.width = min(0.05 * self.size * len(self._text), 1) self._target.x_position = 0.5 * (1 - self._target.width) _target = Instance(modules.Text) def __target_default(self): """ This is called only if no existing title is found.""" width = min(0.05 * self.size * len(self._text), 1) text = modules.Text( text=self._text, y_position=self.height, x_position=0.5 * (1 - width), ) text.width = width return text def __init__(self, text, **kwargs): self._text = text # This will be used by _size_changed if not 'name' in kwargs: # The name is used as au unique marker to identify the # title. We need to set it ASAP. self.name = kwargs['name'] = 'Title' super(Title, self).__init__(**kwargs) self._target.text = self._text # We need to set position after Text is initiated, as text will # override these positions self._target.y_position = self.height self._size_changed()
class TubeFactory(PipeFactory): """Applies the Tube mayavi filter to the given VTK object.""" _target = Instance(filters.Tube, ()) tube_sides = CInt(6, adapts='filter.number_of_sides', desc="""number of sides of the tubes used to represent the lines.""") tube_radius = CFloat(0.05, adapts='filter.radius', desc="""radius of the tubes used to represent the lines.""")
class Text3D(ModuleFactory): """ Positions text at a 3D location in the scene. **Function signature**:: text3d(x, y, z, text, ...) x, y, and z are the position of the origin of the text. The text is positionned in 3D, in figure coordinnates. """ _target = Instance(modules.Text3D, ()) scale = Either(CFloat(1), CArray(shape=(3, )), help="""The scale of the text, in figure units. Either a float, or 3-tuple of floats.""") orientation = CArray(shape=(3, ), adapts='orientation', desc="""the angles giving the orientation of the text. If the text is oriented to the camera, these angles are referenced to the axis of the camera. If not, these angles are referenced to the z axis.""") orient_to_camera = true(adapts='orient_to_camera', desc="""if the text is kept oriented to the camera, or is pointing in a specific direction, regardless of the camera position.""") def __init__(self, x, y, z, text, **kwargs): """ Override init as for different positional arguments.""" if not 'scale' in kwargs: kwargs['scale'] = 1 super(Text3D, self).__init__(None, **kwargs) self._target.text = text self._target.position = (x, y, z) def _scale_changed(self): scale = self.scale if operator.isNumberType(scale): scale = scale * np.ones((3, )) self._target.scale = scale
class Text(ModuleFactory): """ Adds a text on the figure. **Function signature**:: text(x, y, text, ...) x, and y are the position of the origin of the text. If no z keyword argument is given, x and y are the 2D projection of the figure, they belong to [0, 1]. If a z keyword argument is given, the text is positionned in 3D, in figure coordinnates. """ width = Trait(None, None, CFloat, adapts='width', help="""width of the text.""") z = Trait(None, None, CFloat, help="""Optional z position. When specified, the text is positioned in 3D""") _target = Instance(modules.Text, ()) opacity = CFloat(1, adapts="property.opacity", help="""The opacity of the text.""") def __init__(self, x, y, text, **kwargs): """ Override init as for different positional arguments.""" if 'z' in kwargs and kwargs['z'] is not None: self._target.z_position = kwargs['z'] self._target.position_in_3d = True elif not (x < 1. and x > 0. and y > 0. and y < 1.): raise ValueError('Text positions should be in [0, 1] if no z' 'position is given') super(Text, self).__init__(None, **kwargs) self._target.text = text self._target.x_position = x self._target.y_position = y
class CustomBarChart(Pipeline): """ 2012.2.21 custom version of BarChart. It has two more keyword arguments, x_scale, y_scale, which is in charge of the lateral_scale in the X and Y direction. Plots vertical glyphs (like bars) scaled vertical, to do histogram-like plots. This functions accepts a wide variety of inputs, with positions given in 2-D or in 3-D. **Function signatures**:: barchart(s, ...) barchart(x, y, s, ...) barchart(x, y, f, ...) barchart(x, y, z, s, ...) barchart(x, y, z, f, ...) If only one positional argument is passed, it can be a 1-D, 2-D, or 3-D array giving the length of the vectors. The positions of the data points are deducted from the indices of array, and an uniformly-spaced data set is created. If 3 positional arguments (x, y, s) are passed the last one must be an array s, or a callable, f, that returns an array. x and y give the 2D coordinates of positions corresponding to the s values. If 4 positional arguments (x, y, z, s) are passed, the 3 first are arrays giving the 3D coordinates of the data points, and the last one is an array s, or a callable, f, that returns an array giving the data value. """ _source_function = Callable(vertical_vectors_source) _pipeline = [VectorsFactory, ] mode = Trait('cube', bar_mode_dict, desc='The glyph used to represent the bars.') lateral_scale = CFloat(0.9, desc='The lateral scale of the glyph, ' 'in units of the distance between nearest points') def __call__(self, *args, **kwargs): """ Override the call to be able to scale automaticaly the axis. """ g = Pipeline.__call__(self, *args, **kwargs) gs = g.glyph.glyph_source # Use a cube source for glyphs. if not 'mode' in kwargs: gs.glyph_source = gs.glyph_list[-1] # Position the glyph tail on the point. gs.glyph_position = 'tail' gs.glyph_source.center = (0.0, 0.0, 0.5) g.glyph.glyph.orient = False if not 'color' in kwargs: g.glyph.color_mode = 'color_by_scalar' if not 'scale_mode' in kwargs: g.glyph.scale_mode = 'scale_by_vector_components' g.glyph.glyph.clamping = False x, y, z = g.mlab_source.x, g.mlab_source.y, g.mlab_source.z scale_factor = g.glyph.glyph.scale_factor* \ tools._min_axis_distance(x, y, z) x_scale = kwargs.pop('x_scale', self.lateral_scale) y_scale = kwargs.pop('y_scale', self.lateral_scale) try: g.glyph.glyph_source.glyph_source.y_length = \ y_scale/(scale_factor) g.glyph.glyph_source.glyph_source.x_length = \ x_scale/(scale_factor) except TraitError: " Not all types of glyphs have controlable y_length and x_length" return g def get_all_traits(self): """ 2012.2.21 this function determines the set of possible keys in kwargs. add two keywords, x_scale, y_scale. Returns all the traits of class, and the classes in the pipeline. """ #call the parental one first. traits = Pipeline.get_all_traits(self) traits['x_scale'] = Trait(0.9, desc='The scale of the glyph on the x-axis, ' 'in units of the distance between nearest points') traits['y_scale'] = Trait(0.9, desc='The scale of the glyph on the y-axis, ' 'in units of the distance between nearest points') return traits
class Mesh(Pipeline): """ Plots a surface using grid-spaced data supplied as 2D arrays. **Function signatures**:: mesh(x, y, z, ...) x, y, z are 2D arrays, all of the same shape, giving the positions of the vertices of the surface. The connectivity between these points is implied by the connectivity on the arrays. For simple structures (such as orthogonal grids) prefer the `surf` function, as it will create more efficient data structures. For mesh defined by triangles rather than regular implicit connectivity, see the `triangular_mesh` function. """ scale_mode = Trait('none', { 'none': 'data_scaling_off', 'scalar': 'scale_by_scalar', 'vector': 'scale_by_vector' }, help="""the scaling mode for the glyphs ('vector', 'scalar', or 'none').""") scale_factor = CFloat(0.05, desc="""scale factor of the glyphs used to represent the vertices, in fancy_mesh mode. """) tube_radius = Trait(0.025, CFloat, None, help="""radius of the tubes used to represent the lines, in mesh mode. If None, simple lines are used. """) scalars = Array(help="""optional scalar data.""") mask = Array(help="boolean mask array to suppress some data points.") representation = Trait( 'surface', 'wireframe', 'points', 'mesh', 'fancymesh', desc="""the representation type used for the surface.""") _source_function = Callable(grid_source) _pipeline = [ ExtractEdgesFactory, GlyphFactory, TubeFactory, SurfaceFactory ] def __call_internal__(self, *args, **kwargs): """ Override the call to be able to choose whether to apply filters. """ self.source = self._source_function(*args, **kwargs) kwargs.pop('name', None) self.store_kwargs(kwargs) # Copy the pipeline so as not to modify it for the next call self.pipeline = self._pipeline[:] if not self.kwargs['representation'] in ('mesh', 'fancymesh'): self.pipeline.remove(ExtractEdgesFactory) self.pipeline.remove(TubeFactory) self.pipeline.remove(GlyphFactory) self.pipeline = [ PolyDataNormalsFactory, ] + self.pipeline else: if self.kwargs['tube_radius'] == None: self.pipeline.remove(TubeFactory) if not self.kwargs['representation'] == 'fancymesh': self.pipeline.remove(GlyphFactory) self.kwargs['representation'] = 'surface' return self.build_pipeline()
class BarChart(Pipeline): """ Plots vertical glyphs (like bars) scaled vertical, to do histogram-like plots. This functions accepts a wide variety of inputs, with positions given in 2-D or in 3-D. **Function signatures**:: barchart(s, ...) barchart(x, y, s, ...) barchart(x, y, f, ...) barchart(x, y, z, s, ...) barchart(x, y, z, f, ...) If only one positional argument is passed, it can be a 1-D, 2-D, or 3-D array giving the length of the vectors. The positions of the data points are deducted from the indices of array, and an uniformly-spaced data set is created. If 3 positional arguments (x, y, s) are passed the last one must be an array s, or a callable, f, that returns an array. x and y give the 2D coordinates of positions corresponding to the s values. If 4 positional arguments (x, y, z, s) are passed, the 3 first are arrays giving the 3D coordinates of the data points, and the last one is an array s, or a callable, f, that returns an array giving the data value. """ _source_function = Callable(vertical_vectors_source) _pipeline = [ VectorsFactory, ] mode = Trait('cube', bar_mode_dict, desc='The glyph used to represent the bars.') lateral_scale = CFloat(0.9, desc='The lateral scale of the glyph, ' 'in units of the distance between nearest points') auto_scale = true(desc='whether to compute automatically the ' 'lateral scaling of the glyphs. This might be ' 'computationally expensive.') def __call_internal__(self, *args, **kwargs): """ Override the call to be able to scale automatically the axis. """ g = Pipeline.__call_internal__(self, *args, **kwargs) gs = g.glyph.glyph_source # Use a cube source for glyphs. if not 'mode' in kwargs: gs.glyph_source = gs.glyph_dict['cube_source'] # Position the glyph tail on the point. gs.glyph_position = 'tail' gs.glyph_source.center = (0.0, 0.0, 0.5) g.glyph.glyph.orient = False if not 'color' in kwargs: g.glyph.color_mode = 'color_by_scalar' if not 'scale_mode' in kwargs: g.glyph.scale_mode = 'scale_by_vector_components' g.glyph.glyph.clamping = False # The auto-scaling code. It involves finding the minimum # distance between points, which can be very expensive. We # shortcut this calculation for structured data if len(args) == 1 or self.auto_scale: min_axis_distance = 1 else: x, y, z = g.mlab_source.x, g.mlab_source.y, g.mlab_source.z min_axis_distance = \ tools._min_axis_distance(x, y, z) scale_factor = g.glyph.glyph.scale_factor * min_axis_distance lateral_scale = kwargs.pop('lateral_scale', self.lateral_scale) try: g.glyph.glyph_source.glyph_source.y_length = \ lateral_scale/(scale_factor) g.glyph.glyph_source.glyph_source.x_length = \ lateral_scale/(scale_factor) except TraitError: " Not all types of glyphs have controlable y_length and x_length" return g
class Loop(HasTraits): """ A current loop class. """ #------------------------------------------------------------------------- # Public traits #------------------------------------------------------------------------- direction = Array(float, value=(0, 0, 1), cols=3, shape=(3, ), desc='directing vector of the loop', enter_set=True, auto_set=False) radius = CFloat(0.1, desc='radius of the loop', enter_set=True, auto_set=False) position = Array(float, value=(0, 0, 0), cols=3, shape=(3, ), desc='position of the center of the loop', enter_set=True, auto_set=False) _plot = None Bnorm = Property(depends_on='direction,position,radius') view = View('position', 'direction', 'radius', '_') #------------------------------------------------------------------------- # Loop interface #------------------------------------------------------------------------- def base_vectors(self): """ Returns 3 orthognal base vectors, the first one colinear to the axis of the loop. """ # normalize n n = self.direction / (self.direction**2).sum(axis=-1) # choose two vectors perpendicular to n # choice is arbitrary since the coil is symetric about n if np.abs(n[0]) == 1: l = np.r_[n[2], 0, -n[0]] else: l = np.r_[0, n[2], -n[1]] l /= (l**2).sum(axis=-1) m = np.cross(n, l) return n, l, m @on_trait_change('Bnorm') def redraw(self): if hasattr(self, 'app') and self.app.scene._renderer is not None: self.display() self.app.visualize_field() def display(self): """ Display the coil in the 3D view. """ n, l, m = self.base_vectors() theta = np.linspace(0, 2 * np.pi, 30)[..., np.newaxis] coil = self.radius * (np.sin(theta) * l + np.cos(theta) * m) coil += self.position coil_x, coil_y, coil_z = coil.T if self._plot is None: self._plot = self.app.scene.mlab.plot3d(coil_x, coil_y, coil_z, tube_radius=0.007, color=(0, 0, 1), name='Coil') else: self._plot.mlab_source.set(x=coil_x, y=coil_y, z=coil_z) def _get_Bnorm(self): """ returns the magnetic field for the current loop calculated from eqns (1) and (2) in Phys Rev A Vol. 35, N 4, pp. 1535-1546; 1987. """ ### Translate the coordinates in the coil's frame n, l, m = self.base_vectors() R = self.radius r0 = self.position r = np.c_[np.ravel(X), np.ravel(Y), np.ravel(Z)] # transformation matrix coil frame to lab frame trans = np.vstack((l, m, n)) r -= r0 #point location from center of coil r = np.dot(r, linalg.inv(trans)) #transform vector to coil frame #### calculate field # express the coordinates in polar form x = r[:, 0] y = r[:, 1] z = r[:, 2] rho = np.sqrt(x**2 + y**2) theta = np.arctan2(x, y) E = special.ellipe((4 * R * rho) / ((R + rho)**2 + z**2)) K = special.ellipk((4 * R * rho) / ((R + rho)**2 + z**2)) Bz = 1 / np.sqrt((R + rho)**2 + z**2) * (K + E * (R**2 - rho**2 - z**2) / ((R - rho)**2 + z**2)) Brho = z / (rho * np.sqrt((R + rho)**2 + z**2)) * (-K + E * (R**2 + rho**2 + z**2) / ((R - rho)**2 + z**2)) # On the axis of the coil we get a divided by zero here. This returns a # NaN, where the field is actually zero : Brho[np.isnan(Brho)] = 0 B = np.c_[np.cos(theta) * Brho, np.sin(theta) * Brho, Bz] # Rotate the field back in the lab's frame B = np.dot(B, trans) Bx, By, Bz = B.T Bx = np.reshape(Bx, X.shape) By = np.reshape(By, X.shape) Bz = np.reshape(Bz, X.shape) Bnorm = np.sqrt(Bx**2 + By**2 + Bz**2) # We need to threshold ourselves, rather than with VTK, to be able # to use an ImageData Bmax = 10 * np.median(Bnorm) Bx[Bnorm > Bmax] = np.NAN By[Bnorm > Bmax] = np.NAN Bz[Bnorm > Bmax] = np.NAN Bnorm[Bnorm > Bmax] = np.NAN self.Bx = Bx self.By = By self.Bz = Bz return Bnorm
class ImagePlot(HasTraits): plot = Instance(Plot) meanrate = CFloat(0.0) updater = Instance(UpdateEvents) #Plot properties decay_factor = Range(0., 1) colormap = Enum(color_map_name_dict.keys()) channel = Enum(range(getDefaultMonChannelAddress().nChannels)) #Private properties _cmap = Trait(Greys, Callable) def _colormap_default(self): return 'Greys' def _decay_factor_default(self): return 0.5 def _meanrate_default(self): return 0. traits_view = View(Item('plot', editor=ComponentEditor(), show_label=False), Item('meanrate', label='MeanRate(Hz)'), menubar=MenuBar( Menu(Action(name="Edit Plot", action="edit_plot"), CloseAction, name="File")), handler=Controller, width=500, height=500, resizable=True, title="Aer2DViewer", buttons=['OK']) plot_edit_view = View(Group(Item('decay_factor'), Item('colormap'), Item('channel')), buttons=['OK', 'Cancel']) def __init__(self, dims=(128, 10)): super(ImagePlot, self).__init__() z = numpy.zeros(dims) self.plotdata = ArrayPlotData(imagedata=z) plot = Plot(self.plotdata) plot.img_plot("imagedata", xbounds=(0, dims[1]), ybounds=(0, dims[0]), colormap=self._cmap) self.plot = plot self.flag = True def _colormap_changed(self): self._cmap = color_map_name_dict[self.colormap] if hasattr(self, 'plot'): value_range = self.plot.color_mapper.range self.plot.color_mapper = self._cmap(value_range) self.plot.request_redraw() def _channel_changed(self): print('Switching to channel: %d' % self.channel) self.updater.channel = self.channel self.updater.z = self.updater.z * 0 try: self.updater.eventsQueue.stop() except: pass pyNCS.pyST.STas.addrBuildHashTable(self.updater.stcs[self.channel]) self.updater.eventsQueue = pyAex.aexclient.AEXMonClient( MonChannelAddress=self.updater.stcs, channels=[self.channel], host=self.updater.host, port=self.updater.port, autostart=True, fps=self.updater.fps)
class ImagePlot(HasTraits): plot = Instance(Plot) meanrate = CFloat(0.0) updater = Instance(UpdateEvents) #Plot properties tDuration = Range(0., 20) channel = Enum(range(getDefaultMonChannelAddress().nChannels)) def _tDuration_default(self): return 5. def _meanrate_default(self): return 0. traits_view = View(Item('plot', editor=ComponentEditor(), show_label=False), Item('meanrate', label='MeanRate(Hz)'), menubar=MenuBar( Menu(Action(name="Edit Plot", action="edit_plot"), CloseAction, name="File")), handler=Controller, width=800, height=500, resizable=True, title="Aer1DViewer", buttons=['OK']) plot_edit_view = View( Group( Item('tDuration'), #Item('colormap'), Item('channel')), buttons=['OK', 'Cancel']) def __init__(self, dims=(128, 10)): super(ImagePlot, self).__init__() #z = numpy.zeros(dims) self.plotdata = ArrayPlotData(neurons=[0], times=[0]) plot = Plot(self.plotdata) plot.plot( ("times", "neurons"), type="scatter", marker="dot", marker_size=1, color='black', ) self.plot = plot def _channel_changed(self): print('Switching to channel: %d' % self.channel) self.updater.channel = self.channel self.updater.tot_neurons = [] self.updater.tot_times = [] try: self.updater.eventsQueue.stop() except: pass pyNCS.pyST.STas.addrBuildHashTable(self.updater.stcs[self.channel]) self.updater.eventsQueue = pyAex.aexclient.AEXMonClient( MonChannelAddress=self.updater.stcs, channels=[self.channel], host=self.updater.host, port=self.updater.port, autostart=True, fps=self.updater.fps)