forked from enthought/chaco
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpolar_line_renderer.py
190 lines (155 loc) · 5.88 KB
/
polar_line_renderer.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
""" Defines the PolarLineRenderer class.
"""
from __future__ import with_statement
# 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 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
return
def _data_changed(self):
self._cache_valid = False
return
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()
return
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.
"""
# Simple compatibility with new-style rendering loop
return self._draw_component(*args, **kw)
def _draw_component(self, gc, view_bounds=None, mode='normal'):
""" Renders the component.
"""
self._gather_points()
self._render(gc, self._cached_data_pts)
def _bounds_changed(self, old, new):
super(PolarLineRenderer, self)._bounds_changed(old, new)
self._update_mappers()
def _bounds_items_changed(self, event):
super(PolarLineRenderer, self)._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()
return
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()
return