Repository: enthought/chaco

Issue:
Re-export PolarLineRenderer and PolarMapper from chaco.api
Ref [`PolarLineRenderer`](https://github.com/enthought/chaco/blob/c758a65222a2caef9a3d2c074ae9dd5775a3a77e/chaco/polar_line_renderer.py), [`PolarMapper`](https://github.com/enthought/chaco/blob/c758a65222a2caef9a3d2c074ae9dd5775a3a77e/chaco/polar_mapper.py)


Discussion:
ref #47 

Resolving patch:
diff --git a/chaco/api.py b/chaco/api.py
--- a/chaco/api.py
+++ b/chaco/api.py
@@ -54,6 +54,7 @@
 - :class:`~.DiscreteColorMapper`
 - :class:`~.TransformColorMapper`
 - :class:`~.BandedMapper`
+- :class:`~.PolarMapper`
 
 Visual Components
 -----------------
@@ -108,6 +109,7 @@
 - :class:`~.TextPlot1D`
 - :class:`~.SegmentPlot`
 - :class:`~.TextPlot`
+- :class:`~.PolarLineRenderer`
 
 Plot Factories
 --------------
@@ -290,6 +292,7 @@
 from .discrete_color_mapper import DiscreteColorMapper
 from .transform_color_mapper import TransformColorMapper
 from .horizon_plot import BandedMapper
+from .polar_mapper import PolarMapper
 
 # Visual components
 from .abstract_plot_renderer import AbstractPlotRenderer
@@ -351,6 +354,7 @@
 from .text_plot_1d import TextPlot1D
 from .segment_plot import SegmentPlot
 from .text_plot import TextPlot
+from .polar_line_renderer import PolarLineRenderer
 
 # Plot factories
 from .plot_factory import (
diff --git a/chaco/polar_mapper.py b/chaco/polar_mapper.py
--- a/chaco/polar_mapper.py
+++ b/chaco/polar_mapper.py
@@ -4,7 +4,7 @@
 """
 
 # Major library imports
-from numpy import array
+from numpy import array, float64, full_like, ndarray
 
 # Enthought library imports
 from traits.api import Bool, Float
@@ -12,11 +12,11 @@
 # Local relative imports
 from .abstract_mapper import AbstractMapper
 
-###############################################################
-# same as linear mapper at the moment... to be modified later #
-###############################################################
+
 class PolarMapper(AbstractMapper):
     """
+    Same as linear mapper at the moment... to be modified later
+
     Maps a 1-D data space to and from screen space by specifying a range in
     data space and a corresponding fixed line in screen space.
 
@@ -29,8 +29,11 @@ class PolarMapper(AbstractMapper):
     # Private traits
     # ------------------------------------------------------------------------
 
-    _scale = Float(1.0)  # number of screen space units per data space unit
+    # Number of screen space units per data space unit.
+    _scale = Float(1.0)
+    # Is the range of the screen space empty?
     _null_screen_range = Bool(False)
+    # Is the range of the data space empty?
     _null_data_range = Bool(False)
 
     # ------------------------------------------------------------------------
@@ -40,27 +43,38 @@ class PolarMapper(AbstractMapper):
     def map_screen(self, data_array):
         """map_screen(data_array) -> screen_array
 
-        Converts radius and theta values from *data_array*
-        to x and y values and then maps
-        values from data space into screen space.
+        Overrides AbstractMapper. Maps values from data space into screen space.
         """
         self._compute_scale()
         if self._null_data_range:
-            return array([self.low_pos] * len(data_array))
+            if isinstance(data_array, (tuple, list, ndarray)):
+                return full_like(data_array, self.low_pos, dtype=float64)
+            else:
+                return array([self.low_pos])
         else:
             return (data_array - self.range.low) * self._scale + self.low_pos
 
     def map_data(self, screen_val):
         """map_data(screen_val) -> data_val
 
-        Maps values from screen space into data space.
+        Overrides AbstractMapper. Maps values from screen space into data space.
         """
         self._compute_scale()
         if self._null_screen_range:
             return array([self.range.low])
+        elif self._null_data_range:
+            return array([self.range.low])
         else:
             return (screen_val - self.low_pos) / self._scale + self.range.low
 
+    def map_data_array(self, screen_vals):
+        """map_data_array(screen_vals) -> data_vals
+
+        Overrides AbstractMapper. Maps an array of values from screen space
+        into data space.
+        """
+        return self.map_data(screen_vals)
+
     # ------------------------------------------------------------------------
     # Private methods
     # ------------------------------------------------------------------------
@@ -73,9 +87,9 @@ def _compute_scale(self):
             self._cache_valid = False
             return
 
-        d = self.range
+        r = self.range
         screen_range = self.high_pos - self.low_pos
-        data_range = self._pol_to_rect(d.high) - self._pol_to_rect(d.low)
+        data_range = r.high - r.low
         if screen_range == 0.0:
             self._null_screen_range = True
         else:
@@ -84,6 +98,10 @@ def _compute_scale(self):
             self._null_data_range = True
         else:
             self._scale = screen_range / data_range
-            self._null_data_range = False
+            # The screen_range might be small enough that dividing by the
+            # data_range causes it to go to 0. Explicitly call bool because
+            # _scale might also be a numpy scalar and yield another numpy scalar
+            # that the Bool trait rejects.
+            self._null_data_range = bool(self._scale == 0.0)
 
         self._cache_valid = True


Test patch: