class SmithResistanceGrid(AbstractOverlay): # The color of the axis line. grid_color = ColorTrait("gray") # The line thickness (in pixels) of the axis line. grid_weight = Float(1.0) # The dash style of the axis line. grid_style = LineStyle('dot') def overlay(self, component, gc, view_bounds=None, mode='normal'): """ Draws this component overlaid on another component. Overrides AbstractOverlay. """ if not self.visible: return self._draw_component(gc, view_bounds, mode, component) return def _draw_component(self, gc, view_bounds=None, mode='normal', component=None): """ Draws the component. This method is preserved for backwards compatibility. Overrides PlotComponent. """ if not self.visible: return with gc: gc.set_stroke_color(self.grid_color_) gc.set_line_width(self.grid_weight) gc.set_line_dash(self.grid_style_) map_x = self.x_mapper.map_screen map_y = self.y_mapper.map_screen gc.clip_to_rect( map_x(self.x_mapper.range.low), map_y(self.y_mapper.range.low), map_x(self.x_mapper.range.high) - map_x(self.x_mapper.range.low), map_y(self.y_mapper.range.high) - map_y(self.y_mapper.range.low)) x_center = self.x_mapper.map_screen(0) y_center = self.y_mapper.map_screen(0) radius = self.y_mapper.map_screen(1) - self.y_mapper.map_screen(0) # TODO: adapt to zoom level (fixed number of pixels of spacing) for i in range(10): r = i * radius / 10 gc.arc(x_center + r, y_center, radius - r, 0, 2 * pi) gc.stroke_path()
class MyLineInspector(DragTool): # The axis that this tool is parallel to. axis = Enum("index", "value", "index_x", "index_y") # The possible inspection modes of the tool. # # space: # The tool maps from screen space into the data space of the plot. # indexed: # The tool maps from screen space to an index into the plot's index array. inspect_mode = Enum("space", "indexed") # Respond to user mouse events? is_interactive = Bool(True) # Does the tool respond to updates in the metadata on the data source # and update its own position? is_listener = Bool(False) # If interactive, does the line inspector write the current data space point # to the appropriate data source's metadata? write_metadata = Bool(False) # The name of the metadata field to listen or write to. metadata_name = Str("selections") callback = Any() token = Any() handle_size = Float(5) #------------------------------------------------------------------------ # Override default values of inherited traits in BaseTool #------------------------------------------------------------------------ # This tool is visible (overrides BaseTool). visible = True # This tool is drawn as an overlay (overrides BaseTool). draw_mode = "overlay" # TODO:STYLE # Color of the line. color = ColorTrait("grey") # Width in pixels of the line. line_width = Float(1.0) # Dash style of the line. line_style = LineStyle("dash") index_position = Float(-1) # Last recorded position of the mouse _last_position = Trait(None, Any) def draw(self, gc, view_bounds=None): """ Draws this tool on a graphics context. Overrides BaseTool. """ # We draw at different points depending on whether or not we are # interactive. If both listener and interactive are true, then the # selection metadata on the plot component takes precendence. plot = self.component if plot is None: return # if self.is_listener: # tmp = self._get_screen_pts() # elif self.is_interactive: # tmp = self._last_position # # if tmp: # sx, sy = tmp # else: # return if self.axis == "index" or self.axis == "index_x": if self.index_position == -1: self.index_position = int((plot.x + plot.x2) / 2) self.index_changed() self._draw_vertical_line(gc, self.index_position) else: if self.index_position == -1: self.index_position = int((plot.y + plot.y2) / 2) self.index_changed() self._draw_horizontal_line(gc, self.index_position) return def do_layout(self, *args, **kw): pass def overlay(self, component, gc, view_bounds=None, mode="normal"): """ Draws this component overlaid on a graphics context. """ self.draw(gc, view_bounds) return def is_draggable(self, x, y): """ Returns whether the (x,y) position is in a region that is OK to drag. Used by the tool to determine when to start a drag. """ if self.axis == "index" or self.axis == "index_x": if x > self.index_position - self.handle_size and \ x < self.index_position + self.handle_size and \ y > self.component.y2 - 2*self.handle_size and \ y < self.component.y2: return True else: if y > self.index_position - self.handle_size and \ y < self.index_position + self.handle_size and \ x > self.component.x2 - 2*self.handle_size and \ x < self.component.x2: return True return False def dragging(self, event): """ This method is called for every mouse_move event that the tool receives while the user is dragging the mouse. It is recommended that subclasses do most of their work in this method. """ if event.x < self.component.x or event.x > self.component.x2 or \ event.y < self.component.y or event.y > self.component.y2: return if self.axis == "index" or self.axis == "index_x": self.index_position = event.x else: self.index_position = event.y self.index_changed() # index = self.component.map_index((event.x,event.y),index_only=True) # if self.axis == "index" or self.axis == "index_x": # self.index_position = event.x ## self.component.request_redraw() # self.callback(self, self.axis, index[0]) # else: # self.index_position = event.y ## self.component.request_redraw() # self.callback(self, self.axis, index[1]) def index_changed(self): plot = self.component if self.axis == "index" or self.axis == "index_x": index = plot.map_index((self.index_position, plot.y), index_only=True) value = plot.map_data([(self.index_position, plot.y)]) self.callback(self, self.axis, index[0], value[0][0]) else: index = plot.map_index((plot.x, self.index_position), index_only=True) value = plot.map_data([(plot.x, self.index_position)]) self.callback(self, self.axis, index[1], value[0][1]) def update_index(self, token, axis, index): if token == self.token and axis == self.axis: plot = self.component if self.axis == "index" or self.axis == "index_x": dx = plot.index.get_data()[0].get_data()[index] dy = plot.index.get_data()[1].get_data()[0] sx = plot.map_screen([(dx, dy)])[0][0] self.index_position = sx self.component.request_redraw() else: dx = plot.index.get_data()[0].get_data()[0] dy = plot.index.get_data()[1].get_data()[index] sy = plot.map_screen([(dx, dy)])[0][1] self.index_position = sy self.component.request_redraw() # if self.index_position != index: # self.index_position = index pass def _draw_vertical_line(self, gc, sx): """ Draws a vertical line through screen point (sx,sy) having the height of the tool's component. """ if sx < self.component.x or sx > self.component.x2: return gc.save_state() try: gc.set_stroke_color(self.color_) gc.set_line_width(self.line_width) gc.set_line_dash(self.line_style_) gc.move_to(sx, self.component.y) gc.line_to(sx, self.component.y2) gc.stroke_path() gc.rect(sx - self.handle_size, self.component.y2 - 2 * self.handle_size, 2 * self.handle_size, 2 * self.handle_size) gc.fill_path() finally: gc.restore_state() return def _draw_horizontal_line(self, gc, sy): """ Draws a horizontal line through screen point (sx,sy) having the width of the tool's component. """ if sy < self.component.y or sy > self.component.y2: return gc.save_state() try: gc.set_stroke_color(self.color_) gc.set_line_width(self.line_width) gc.set_line_dash(self.line_style_) gc.move_to(self.component.x, sy) gc.line_to(self.component.x2, sy) gc.stroke_path() gc.rect(self.component.x2 - 2 * self.handle_size, sy - self.handle_size, 2 * self.handle_size, 2 * self.handle_size) gc.fill_path() finally: gc.restore_state() return
class SmithCircle(AbstractOverlay): # The color of the axis line. line_color = ColorTrait("black") # The line thickness (in pixels) of the axis line. line_weight = Float(1.0) # The dash style of the axis line. line_style = LineStyle('solid') # def __init__(self, component=None, **kwargs): # # Override init so that our component gets set last. We want the # # _component_changed() event handler to get run last. # super(self.__class__, self).__init__(**kwargs) # if component is not None: # self.component = component def overlay(self, component, gc, view_bounds=None, mode='normal'): """ Draws this component overlaid on another component. Overrides AbstractOverlay. """ if not self.visible: return self._draw_component(gc, view_bounds, mode, component) return # def _draw_overlay(self, gc, view_bounds=None, mode='normal'): # """ Draws the overlay layer of a component. # Overrides PlotComponent. # """ # self._draw_component(gc, view_bounds, mode) # return def _draw_component(self, gc, view_bounds=None, mode='normal', component=None): """ Draws the component. This method is preserved for backwards compatibility. Overrides PlotComponent. """ if not self.visible: return def center_radius( mapper): #data_low, data_high, screen_low, screen_high): map = mapper.map_screen return map(0), map(1) - map(0) with gc: gc.set_stroke_color(self.line_color_) gc.set_line_width(self.line_weight) gc.set_line_dash(self.line_style_) map_x = self.x_mapper.map_screen map_y = self.y_mapper.map_screen gc.clip_to_rect( map_x(self.x_mapper.range.low), map_y(self.y_mapper.range.low), map_x(self.x_mapper.range.high) - map_x(self.x_mapper.range.low), map_y(self.y_mapper.range.high) - map_y(self.y_mapper.range.low)) x_center, radius = center_radius(self.x_mapper) y_center, radius = center_radius(self.y_mapper) # outer ring gc.arc(x_center, y_center, radius, 0, 2 * pi) gc.stroke_path() # horizontal axis gc.move_to(x_center - radius, y_center) gc.line_to(x_center + radius, y_center) gc.stroke_path()
class SmithReactanceGrid(AbstractOverlay): # The color of the axis line. grid_color = ColorTrait("gray") # The line thickness (in pixels) of the axis line. grid_weight = Float(1.0) # The dash style of the axis line. grid_style = LineStyle('dot') def overlay(self, component, gc, view_bounds=None, mode='normal'): """ Draws this component overlaid on another component. Overrides AbstractOverlay. """ if not self.visible: return self._draw_component(gc, view_bounds, mode, component) return def _draw_component(self, gc, view_bounds=None, mode='normal', component=None): """ Draws the component. This method is preserved for backwards compatibility. Overrides PlotComponent. """ if not self.visible: return with gc: gc.set_stroke_color(self.grid_color_) gc.set_line_width(self.grid_weight) gc.set_line_dash(self.grid_style_) map_x = self.x_mapper.map_screen map_y = self.y_mapper.map_screen gc.clip_to_rect( map_x(self.x_mapper.range.low), map_y(self.y_mapper.range.low), map_x(self.x_mapper.range.high) - map_x(self.x_mapper.range.low), map_y(self.y_mapper.range.high) - map_y(self.y_mapper.range.low)) x_center = self.x_mapper.map_screen(0) y_center = self.y_mapper.map_screen(0) rad = self.y_mapper.map_screen(1) - self.y_mapper.map_screen(0) # TODO: adapt to zoom level (fixed number of pixels of spacing) rad2 = rad**2 for i in range(6): r1 = i * rad / 5 tmp = (rad2 - r1**2) / (rad2 + r1**2) gc.arc(x_center + rad, y_center + r1, r1, pi - arcsin(tmp), 1.5 * pi) gc.stroke_path() gc.arc(x_center + rad, y_center - r1, r1, 0.5 * pi, pi + arcsin(tmp)) gc.stroke_path() stoprad = 2 * rad / 10 stoprad2 = stoprad**2 for i in range(6): r2 = 7 * rad / (i + 1) tmp = (rad2 - r2**2) / (rad2 + r2**2) tmp2 = (stoprad2 - r2**2) / (stoprad2 + r2**2) gc.arc(x_center + rad, y_center + r2, r2, pi - arcsin(tmp), pi - arcsin(tmp2)) gc.stroke_path() gc.arc(x_center + rad, y_center - r2, r2, pi + arcsin(tmp2), pi + arcsin(tmp)) gc.stroke_path()
class SegmentPlot(BaseXYPlot): """ A plot consisting of disconnected line segments. """ # The color of the line. color = black_color_trait # The color to use to highlight the line when selected. selected_color = ColorTrait("lightyellow") # The style of the selected line. selected_line_style = LineStyle("solid") # The name of the key in self.metadata that holds the selection mask metadata_name = Str("selections") # The thickness of the line. line_width = Float(1.0) # The line dash style. line_style = LineStyle # Traits UI View for customizing the plot. traits_view = tui.View(tui.Item("color", style="custom"), "line_width", "line_style", buttons=["OK", "Cancel"]) #------------------------------------------------------------------------ # Private traits #------------------------------------------------------------------------ # Cached list of non-NaN arrays of (x,y) data-space points; regardless of # self.orientation, this is always stored as (index_pt, value_pt). This is # different from the default BaseXYPlot definition. _cached_data_pts = List # Cached list of non-NaN arrays of (x,y) screen-space points. _cached_screen_pts = List def hittest(self, screen_pt, threshold=7.0): # NotImplemented return None def get_screen_points(self): self._gather_points() return [self.map_screen(ary) for ary in self._cached_data_pts] #------------------------------------------------------------------------ # Private methods; implements the BaseXYPlot stub methods #------------------------------------------------------------------------ def _gather_points(self): """ Collects the data points that are within the bounds of the plot and caches them. """ if self._cache_valid or not self.index or not self.value: return index = self.index.get_data() value = self.value.get_data() # Check to see if the data is completely outside the view region for ds, rng in ((self.index, self.index_range), (self.value, self.value_range)): low, high = ds.get_bounds() if low > rng.high or high < rng.low: return if len(index) == 0 or len(value) == 0 or len(index) != len(value): self._cached_data_pts = [] self._cache_valid = True size_diff = len(value) - len(index) if size_diff > 0: warnings.warn('len(value) %d - len(index) %d = %d' \ % (len(value), len(index), size_diff)) index_max = len(index) value = value[:index_max] else: index_max = len(value) index = index[:index_max] if index_max % 2: # We need an even number of points. Exclude the final one and # continue. warnings.warn('need an even number of points; got %d' % index_max) index = index[:index_max - 1] value = value[:index_max - 1] # TODO: restore the functionality of rendering highlighted portions # of the line #selection = self.index.metadata.get(self.metadata_name, None) #if selection is not None and type(selection) in (ndarray, list) and \ # len(selection) > 0: # Exclude NaNs and Infs. finite_mask = np.isfinite(value) & np.isfinite(index) # Since the line segment ends are paired, we need to exclude the whole pair if # one is not finite. finite_mask[::2] &= finite_mask[1::2] finite_mask[1::2] &= finite_mask[::2] self._cached_data_pts = [ np.column_stack([index[finite_mask], value[finite_mask]]) ] self._cache_valid = True def _render(self, gc, points, selected_points=None): if len(points) == 0: return gc.save_state() try: gc.set_antialias(True) gc.clip_to_rect(self.x, self.y, self.width, self.height) if selected_points is not None: self._render_segments(gc, selected_points, self.selected_color_, self.line_width + 10.0, self.selected_line_style_) # Render using the normal style self._render_segments(gc, points, self.color_, self.line_width, self.line_style_) finally: gc.restore_state() def _render_segments(self, gc, points, color, line_width, line_style): gc.set_stroke_color(color) gc.set_line_width(line_width) gc.set_line_dash(line_style) gc.begin_path() for ary in points: if len(ary) > 0: gc.line_set(ary[::2], ary[1::2]) gc.stroke_path() @on_trait_change('color,line_style,line_width') def _redraw(self): self.invalidate_draw() self.request_redraw()