Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit eec90cc

Browse files
committed
Merge pull request plotly#95 from plotly/iss87
Iss87
2 parents 55b942d + 34e3168 commit eec90cc

File tree

2 files changed

+83
-94
lines changed

2 files changed

+83
-94
lines changed

plotly/matplotlylib/mpltools.py

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -318,24 +318,14 @@ def get_spine_visible(ax, spine_key):
318318
return False # oh man, and i thought we exhausted the options...
319319

320320

321-
def is_bar(**props):
321+
def is_bar(bar_containers, **props):
322322
"""A test to decide whether a path is a bar from a vertical bar chart."""
323-
tests = []
324-
tests += get_rect_ymin(props['data']) == 0,
325-
if all(tests):
326-
return True
327-
else:
328-
return False
329-
330323

331-
def is_barh(**props):
332-
"""A test to decide whether a path is a bar from a horizontal bar chart."""
333-
tests = []
334-
tests += get_rect_xmin(props['data']) == 0,
335-
if all(tests):
336-
return True
337-
else:
338-
return False
324+
# is this patch in a bar container?
325+
for container in bar_containers:
326+
if props['mplobj'] in container:
327+
return True
328+
return False
339329

340330

341331
def make_bar(**props):
@@ -351,7 +341,6 @@ def make_bar(**props):
351341
"""
352342
return {
353343
'bar': props['mplobj'],
354-
'orientation': props['orientation'],
355344
'x0': get_rect_xmin(props['data']),
356345
'y0': get_rect_ymin(props['data']),
357346
'x1': get_rect_xmax(props['data']),

plotly/matplotlylib/renderer.py

Lines changed: 77 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ def __init__(self):
5050
"""
5151
self.plotly_fig = Figure(data=Data(), layout=Layout())
5252
self.mpl_fig = None
53-
self.current_ax_patches = []
53+
self.current_mpl_ax = None
54+
self.bar_containers = None
55+
self.current_bars = []
5456
self.axis_ct = 0
5557
self.mpl_x_bounds = (0, 1)
5658
self.mpl_y_bounds = (0, 1)
@@ -137,6 +139,10 @@ def open_axes(self, ax, props):
137139
138140
"""
139141
self.msg += " Opening axes\n"
142+
self.current_mpl_ax = ax
143+
self.bar_containers = [c for c in ax.containers # empty is OK
144+
if c.__class__.__name__ == 'BarContainer']
145+
self.current_bars = []
140146
self.axis_ct += 1
141147
# set defaults in axes
142148
xaxis = XAxis(
@@ -173,20 +179,27 @@ def close_axes(self, ax):
173179
174180
Bars from bar charts are given to PlotlyRenderer one-by-one,
175181
thus they need to be taken care of at the close of each axes object.
176-
The self.current_ax_patches variable should be empty unless a bar
177-
chart has been created or a rectangle object has been drawn that has
178-
an edge exactly on the lines x=0 or y=0.
182+
The self.current_bars variable should be empty unless a bar
183+
chart has been created.
179184
180185
Positional arguments:
181186
ax -- an mpl axes object, not required at this time.
182187
183188
"""
184-
for patch_coll in self.current_ax_patches:
185-
self.draw_bar(patch_coll)
186-
self.current_ax_patches = [] # clear this for next axes obj
189+
self.draw_bars(self.current_bars)
187190
self.msg += " Closing axes\n"
188191

189-
def draw_bar(self, patch_coll):
192+
def draw_bars(self, bars):
193+
194+
# sort bars according to bar containers
195+
mpl_traces = []
196+
for container in self.bar_containers:
197+
mpl_traces.append([bar_props for bar_props in self.current_bars
198+
if bar_props['mplobj'] in container])
199+
for trace in mpl_traces:
200+
self.draw_bar(trace)
201+
202+
def draw_bar(self, coll):
190203
"""Draw a collection of similar patches as a bar chart.
191204
192205
After bars are sorted, an appropriate data dictionary must be created
@@ -198,30 +211,67 @@ def draw_bar(self, patch_coll):
198211
patch_coll -- a collection of patches to be drawn as a bar chart.
199212
200213
"""
201-
orientation = patch_coll[0]['orientation']
214+
tol = 1e-10
215+
trace = [mpltools.make_bar(**bar_props) for bar_props in coll]
216+
widths = [bar_props['x1'] - bar_props['x0'] for bar_props in trace]
217+
heights = [bar_props['y1'] - bar_props['y0'] for bar_props in trace]
218+
vertical = abs(
219+
sum(widths[0]-widths[iii] for iii in range(len(widths)))
220+
) < tol
221+
horizontal = abs(
222+
sum(heights[0]-heights[iii] for iii in range(len(heights)))
223+
) < tol
224+
if vertical and horizontal:
225+
# Check for monotonic x. Can't both be true!
226+
x_zeros = [bar_props['x0'] for bar_props in trace]
227+
if all((x_zeros[iii+1] > x_zeros[iii]
228+
for iii in range(len(x_zeros[:-1])))):
229+
orientation = 'v'
230+
else:
231+
orientation = 'h'
232+
elif vertical:
233+
orientation = 'v'
234+
else:
235+
orientation = 'h'
202236
if orientation == 'v':
203237
self.msg += " Attempting to draw a vertical bar chart\n"
204-
patch_coll.sort(key=lambda b: b['x0'])
205-
x = [bar['x0']+(bar['x1']-bar['x0'])/2 for bar in patch_coll]
206-
y = [bar['y1'] for bar in patch_coll]
207-
bar_gap = mpltools.get_bar_gap([bar['x0'] for bar in patch_coll],
208-
[bar['x1'] for bar in patch_coll])
238+
old_heights = [bar_props['y1'] for bar_props in trace]
239+
for bar in trace:
240+
bar['y0'], bar['y1'] = 0, bar['y1'] - bar['y0']
241+
new_heights = [bar_props['y1'] for bar_props in trace]
242+
# check if we're stacked or not...
243+
for old, new in zip(old_heights, new_heights):
244+
if abs(old - new) > tol:
245+
self.plotly_fig['layout']['barmode']='stack'
246+
self.plotly_fig['layout']['hovermode']='x'
247+
x = [bar['x0']+(bar['x1']-bar['x0'])/2 for bar in trace]
248+
y = [bar['y1'] for bar in trace]
249+
bar_gap = mpltools.get_bar_gap([bar['x0'] for bar in trace],
250+
[bar['x1'] for bar in trace])
209251
else:
210252
self.msg += " Attempting to draw a horizontal bar chart\n"
211-
patch_coll.sort(key=lambda b: b['y0'])
212-
x = [bar['x1'] for bar in patch_coll]
213-
y = [bar['y0']+(bar['y1']-bar['y0'])/2 for bar in patch_coll]
214-
bar_gap = mpltools.get_bar_gap([bar['y0'] for bar in patch_coll],
215-
[bar['y1'] for bar in patch_coll])
253+
old_rights = [bar_props['x1'] for bar_props in trace]
254+
for bar in trace:
255+
bar['x0'], bar['x1'] = 0, bar['x1'] - bar['x0']
256+
new_rights = [bar_props['x1'] for bar_props in trace]
257+
# check if we're stacked or not...
258+
for old, new in zip(old_rights, new_rights):
259+
if abs(old - new) > tol:
260+
self.plotly_fig['layout']['barmode']='stack'
261+
self.plotly_fig['layout']['hovermode']='y'
262+
x = [bar['x1'] for bar in trace]
263+
y = [bar['y0']+(bar['y1']-bar['y0'])/2 for bar in trace]
264+
bar_gap = mpltools.get_bar_gap([bar['y0'] for bar in trace],
265+
[bar['y1'] for bar in trace])
216266
bar = Bar(orientation=orientation,
217267
x=x,
218268
y=y,
219269
xaxis='x{0}'.format(self.axis_ct),
220270
yaxis='y{0}'.format(self.axis_ct),
221-
opacity=patch_coll[0]['alpha'],
271+
opacity=trace[0]['alpha'], # TODO: get all alphas if array?
222272
marker=Marker(
223-
color=patch_coll[0]['facecolor'],
224-
line=Line(width=patch_coll[0]['edgewidth'])))
273+
color=trace[0]['facecolor'], # TODO: get all
274+
line=Line(width=trace[0]['edgewidth']))) # TODO: get all
225275
if len(bar['x']) > 1:
226276
self.msg += " Heck yeah, I drew that bar chart\n"
227277
self.plotly_fig['data'] += bar,
@@ -232,6 +282,7 @@ def draw_bar(self, patch_coll):
232282
warnings.warn('found box chart data with length <= 1, '
233283
'assuming data redundancy, not plotting.')
234284

285+
235286
def draw_marked_line(self, **props):
236287
"""Create a data dict for a line obj.
237288
@@ -400,65 +451,14 @@ def draw_path(self, **props):
400451
401452
"""
402453
self.msg += " Attempting to draw a path\n"
403-
is_bar = mpltools.is_bar(**props)
404-
is_barh = mpltools.is_barh(**props)
405-
if is_bar: # if we think it's a bar, add it!
406-
self.msg += " Assuming path is a vertical bar\n"
407-
bar = mpltools.make_bar(orientation='v', **props)
408-
self.file_bar(bar)
409-
if is_barh: # perhaps a horizontal bar?
410-
self.msg += " Assuming path is a horizontal bar\n"
411-
bar = mpltools.make_bar(orientation='h', **props)
412-
self.file_bar(bar)
413-
if not (is_bar or is_barh):
454+
is_bar = mpltools.is_bar(self.current_mpl_ax.containers, **props)
455+
if is_bar:
456+
self.current_bars += [props]
457+
else:
414458
self.msg += " This path isn't a bar, not drawing\n"
415459
warnings.warn("I found a path object that I don't think is part "
416460
"of a bar chart. Ignoring.")
417461

418-
def file_bar(self, bar):
419-
"""Puts a given bar into an appropriate bar or barh collection.
420-
421-
Bars come from the mplexporter one-by-one. To try to put them into
422-
appropriate data sets, we must compare them to existing data.
423-
424-
Positional arguments:
425-
bar -- a bar dictionary created in mpltools.make_bar.py.
426-
427-
bar.keys() -- [
428-
'bar', (mpl path object)
429-
'orientation', (bar direction, 'v' or 'h' for horizontal or vertical)
430-
'x0', ([x0, y0] = bottom-left corner of rectangle)
431-
'y0',
432-
'x1', ([x1, y1] = top-right corner of rectangle):
433-
'y1',
434-
'alpha', (opacity of rectangle)
435-
'edgecolor', (boundary line color)
436-
'facecolor', (rectangle color)
437-
'edgewidth', (boundary line width)
438-
'dasharray', (linestyle for boundary line)
439-
'zorder', (precedence when stacked)
440-
]
441-
442-
"""
443-
self.msg += " Putting a bar into the proper bar collection\n"
444-
if len(self.current_ax_patches) == 0:
445-
self.msg += " Started a new bar collection with this " \
446-
"bar\n"
447-
self.current_ax_patches.append([])
448-
self.current_ax_patches[-1] += bar,
449-
else:
450-
match = False
451-
for patch_collection in self.current_ax_patches:
452-
if mpltools.check_bar_match(patch_collection[0], bar):
453-
match = True
454-
patch_collection += bar,
455-
self.msg += " Filed bar into existing bar " \
456-
"collection\n"
457-
if not match:
458-
self.msg += " Started a new bar collection with " \
459-
"this bar\n"
460-
self.current_ax_patches.append([])
461-
self.current_ax_patches[-1] += bar,
462462

463463
def draw_text(self, **props):
464464
"""Create an annotation dict for a text obj.

0 commit comments

Comments
 (0)