Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
chaco / plots / polar_line_renderer.py
Size: Mime:
# (C) Copyright 2005-2021 Enthought, Inc., Austin, TX
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only under
# the conditions described in the aforementioned license. The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!

""" Defines the PolarLineRenderer class.
"""


# Major library imports
from numpy import array, cos, pi, sin, transpose

# Enthought library imports
from enable.api import black_color_trait, LineStyle
from traits.api import Float

# Local, relative imports
from chaco.abstract_plot_renderer import AbstractPlotRenderer


class PolarLineRenderer(AbstractPlotRenderer):
    """A renderer for polar line plots."""

    # ------------------------------------------------------------------------
    # Appearance-related traits
    # ------------------------------------------------------------------------

    # The color of the origin axis.
    origin_axis_color_ = (0, 0, 0, 1)
    # The width of the origin axis.
    origin_axis_width = 2.0
    # The origin axis is visible.
    origin_axis_visible = True
    # The grid is visible.
    grid_visible = True
    # The orientation of the plot is horizontal; for any other value, it is
    # transposed
    orientation = "h"
    # The color of the line.
    color = black_color_trait
    # The width of the line.
    line_width = Float(1.0)
    # The style of the line.
    line_style = LineStyle("solid")
    # The style of the grid lines.
    grid_style = LineStyle("dot")

    def _gather_points(self):
        """
        Collects the data points that are within the plot bounds and caches them
        """
        # This is just a stub for now.  We should really find the lines only
        # inside the screen range here.

        x = self.index.get_data()
        y = self.value.get_data()
        rad = min(self.width / 2.0, self.height / 2.0)
        sx = x * rad + self.x + self.width / 2.0
        sy = y * rad + self.y + self.height / 2.0

        points = transpose(array((sx, sy)))
        self._cached_data_pts = points
        self._cache_valid = True

    def _data_changed(self):
        self._cache_valid = False

    def _update_mappers(self):
        # Dunno if there is anything else to do here
        self._cache_valid = False

    def _render(self, gc, points):
        """Actually draw the plot."""
        with gc:
            gc.set_antialias(True)
            self._draw_default_axes(gc)
            self._draw_default_grid(gc)
            if len(points) > 0:
                gc.clip_to_rect(self.x, self.y, self.width, self.height)
                gc.set_stroke_color(self.color_)
                gc.set_line_width(self.line_width)
                gc.set_line_dash(self.line_style_)

                gc.begin_path()
                gc.lines(points)
                gc.stroke_path()

    def map_screen(self, data_array):
        """Maps an array of data points into screen space and returns it as
        an array.

        Implements the AbstractPlotRenderer interface.
        """

        if len(data_array) == 0:
            return []
        elif len(data_array) == 1:
            xtmp, ytmp = transpose(data_array)
            x_ary = xtmp
            y_ary = ytmp
        else:
            x_ary, y_ary = transpose(data_array)

        sx = self.index_mapper.map_screen(x_ary)
        sy = self.value_mapper.map_screen(y_ary)

        if self.orientation == "h":
            return transpose(array((sx, sy)))
        else:
            return transpose(array((sy, sx)))

    def map_data(self, screen_pt):
        """Maps a screen space point into the "index" space of the plot.

        Implements the AbstractPlotRenderer interface.
        """
        if self.orientation == "h":
            x, y = screen_pt
        else:
            y, x = screen_pt
        return array(
            (self.index_mapper.map_data(x), self.value_mapper.map_data(y))
        )

    def _downsample(self):
        return self.map_screen(self._cached_data_pts)

    def _draw_plot(self, *args, **kw):
        """Draws the 'plot' layer."""
        self._gather_points()
        self._render(gc, self._cached_data_pts)

    def _bounds_changed(self, old, new):
        super()._bounds_changed(old, new)
        self._update_mappers()

    def _bounds_items_changed(self, event):
        super()._bounds_items_changed(event)
        self._update_mappers()

    def _draw_default_axes(self, gc):
        if not self.origin_axis_visible:
            return

        with gc:
            gc.set_stroke_color(self.origin_axis_color_)
            gc.set_line_width(self.origin_axis_width)
            gc.set_line_dash(self.grid_style_)
            x_data, y_data = transpose(self._cached_data_pts)
            x_center = self.x + self.width / 2.0
            y_center = self.y + self.height / 2.0

            for theta in range(12):
                r = min(self.width / 2.0, self.height / 2.0)
                x = r * cos(theta * pi / 6) + x_center
                y = r * sin(theta * pi / 6) + y_center
                data_pts = array([[x_center, y_center], [x, y]])
                start, end = data_pts
                gc.move_to(int(start[0]), int(start[1]))
                gc.line_to(int(end[0]), int(end[1]))
                gc.stroke_path()

    def _draw_default_grid(self, gc):
        if not self.grid_visible:
            return

        with gc:
            gc.set_stroke_color(self.origin_axis_color_)
            gc.set_line_width(self.origin_axis_width)
            gc.set_line_dash(self.grid_style_)
            x_data, y_data = transpose(self._cached_data_pts)
            x_center = self.x + self.width / 2.0
            y_center = self.y + self.height / 2.0
            rad = min(self.width / 2.0, self.height / 2.0)
            for r_part in range(1, 5):
                r = rad * r_part / 4
                gc.arc(x_center, y_center, r, 0, 2 * pi)
                gc.stroke_path()