diff --git a/demo/moulinrouge/tests.py b/demo/moulinrouge/tests.py index e0424df..9aa5866 100644 --- a/demo/moulinrouge/tests.py +++ b/demo/moulinrouge/tests.py @@ -131,6 +131,13 @@ def get_test_routes(app): graph.add('1', [10, 0, -10]) return graph.render_response() + @app.route('/test/bar') + def test_bar(): + bar = Bar() + bar.add('1', [1, 2, 3]) + bar.add('2', [4, 5, 6]) + return bar.render_response() + @app.route('/test/stacked') def test_stacked(): stacked = StackedBar() diff --git a/pygal/graph/bar.py b/pygal/graph/bar.py index f2e5a9c..12ddcb8 100644 --- a/pygal/graph/bar.py +++ b/pygal/graph/bar.py @@ -33,86 +33,50 @@ class Bar(Graph): self._x_ranges = None super(Bar, self).__init__(*args, **kwargs) - def bar(self, serie_node, serie, values, index, stack_vals=None): + def _bar(self, parent, x, y, index): + width = self.view.x(1 / self._len) + margin = width * .1 * 0 + x += margin + width -= 2 * margin + width /= self._order + x += index * width + padding = width * .005 * 0 + x += padding + width -= 2 * padding + + y_0 = self.view.y(self.zero) + height = abs(y - y_0) + self.svg.transposable_node( + parent, + 'rect', + x=x, + y=y, + rx=self.rounded_bars * 1 if self.rounded_bars else 0, + ry=self.rounded_bars * 1 if self.rounded_bars else 0, + width=width, + height=height, + class_='rect reactive tooltip-trigger') + return (x + width / 2, y + height / 2) + + def bar(self, serie_node, serie, index): """Draw a bar graph for a serie""" - # value here is a list of tuple range of tuple coord - - def view(rng): - """Project range""" - t, T = rng - fun = swap if self.horizontal else ident - return self.view(fun(t)), self.view(fun(T)) - bars = self.svg.node(serie_node['plot'], class_="bars") - view_values = map(view, values) - for i, ((x, y), (X, Y)) in enumerate(view_values): + view_values = map(self.view, serie.points) + for i, (x, y) in enumerate(view_values): if None in (x, y): continue - - # +-------+ - # | | - # | | - # | +-------+ - # | | | - # | | | - # | | | - # +-------+-------+ - # (x,y) (X,Y) - # - # x and y are left range coords and X, Y right ones metadata = serie.metadata.get(i) - val = self._format(values[i][1][1]) - if self.horizontal: - x, y, X, Y = Y, X, y, x - width = X - x - padding = .1 * width - inner_width = width - 2 * padding - if self.horizontal: - height = self.view.x(self.zero) - y - else: - height = self.view.y(self.zero) - y - if stack_vals is None: - bar_width = inner_width / self._order - bar_padding = .1 * bar_width - bar_inner_width = bar_width - 2 * bar_padding - offset = index * bar_width + bar_padding - shift = 0 - else: - offset = 0 - bar_inner_width = inner_width - shift = stack_vals[i][int(height < 0)] - stack_vals[i][int(height < 0)] += height - x = x + padding + offset - - if height < 0: - y = y + height - height = -height - y -= shift bar = decorate( self.svg, self.svg.node(bars, class_='bar'), metadata) - self.svg.transposable_node( - bar, 'rect', - x=x, - y=y, - rx=self.rounded_bars * 1 if self.rounded_bars else 0, - ry=self.rounded_bars * 1 if self.rounded_bars else 0, - width=bar_inner_width, - height=height, - class_='rect reactive tooltip-trigger') - - x += bar_inner_width / 2 - y += height / 2 + val = self._get_value(serie.points, i) - if self.horizontal: - x, y = y, x - - self._tooltip_data(bar, val, x, y, classes="centered") - self._static_value(serie_node, val, x, y) - - return stack_vals + x_center, y_center = self._bar(bar, x, y, index) + self._tooltip_data( + bar, val, x_center, y_center, classes="centered") + self._static_value(serie_node, val, x_center, y_center) def _compute(self): self._box.ymin = min(self._min, self.zero) @@ -120,18 +84,18 @@ class Bar(Graph): x_pos = [ x / self._len for x in range(self._len + 1) ] if self._len > 1 else [0, 1] # Center if only one value + + self._points(x_pos) + y_pos = compute_scale( self._box.ymin, self._box.ymax, self.logarithmic, self.order_min ) if not self.y_labels else map(float, self.y_labels) - self._x_ranges = zip(x_pos, x_pos[1:]) + # self._x_ranges = zip(x_pos, x_pos[1:]) self._x_labels = self.x_labels and zip(self.x_labels, [ sum(x_range) / 2 for x_range in self._x_ranges]) self._y_labels = zip(map(self._format, y_pos), y_pos) def _plot(self): for index, serie in enumerate(self.series): - serie_node = self._serie(index) - self.bar(serie_node, serie, [ - tuple((self._x_ranges[i][j], v) for j in range(2)) - for i, v in enumerate(serie.values)], index) + self.bar(self._serie(index), serie, index) diff --git a/pygal/graph/graph.py b/pygal/graph/graph.py index 3ceca28..dc8beae 100644 --- a/pygal/graph/graph.py +++ b/pygal/graph/graph.py @@ -322,3 +322,15 @@ class Graph(BaseGraph): x=x, y=y + self.value_font_size / 3 ).text = value if self.print_zeroes or value != '0' else '' + + def _get_value(self, values, i): + """Get the value formatted for tooltip""" + return self._format(values[i][1]) + + def _points(self, x_pos): + for serie in self.series: + serie.points = [ + (x_pos[i], v) + for i, v in enumerate(serie.values)] + if self.interpolate: + serie.interpolated = self._interpolate(serie.values, x_pos) diff --git a/pygal/graph/line.py b/pygal/graph/line.py index d4a706b..1c2f9b9 100644 --- a/pygal/graph/line.py +++ b/pygal/graph/line.py @@ -32,10 +32,6 @@ class Line(Graph): self._self_close = False super(Line, self).__init__(*args, **kwargs) - def _get_value(self, values, i): - """Get the value formatted for tooltip""" - return self._format(values[i][1]) - @cached_property def _values(self): return [ @@ -90,14 +86,6 @@ class Line(Graph): serie_node['plot'], view_values, close=self._self_close, class_='line reactive' + (' nofill' if not self.fill else '')) - def _points(self, x_pos): - for serie in self.series: - serie.points = [ - (x_pos[i], v) - for i, v in enumerate(serie.values)] - if self.interpolate: - serie.interpolated = self._interpolate(serie.values, x_pos) - def _compute(self): x_pos = [ x / (self._len - 1) for x in range(self._len) diff --git a/pygal/graph/stackedbar.py b/pygal/graph/stackedbar.py index dbf642f..158ff84 100644 --- a/pygal/graph/stackedbar.py +++ b/pygal/graph/stackedbar.py @@ -57,12 +57,12 @@ class StackedBar(Bar): sum(x_range) / 2 for x_range in self._x_ranges]) self._y_labels = zip(map(self._format, y_pos), y_pos) - def _plot(self): - stack_vals = [[0, 0] for i in range(self._len)] - for index, serie in enumerate(self.series): - serie_node = self._serie(index) - stack_vals = self.bar( - serie_node, serie, [ - tuple((self._x_ranges[i][j], v) for j in range(2)) - for i, v in enumerate(serie.values)], index, - stack_vals) + def _points(self, x_pos): + accumulation = [0] * self._len + for serie in self.series: + accumulation = map(sum, zip(accumulation, serie.values)) + serie.points = [ + (x_pos[i], v) + for i, v in enumerate(accumulation)] + if self.interpolate: + serie.interpolated = self._interpolate(accumulation, x_pos) diff --git a/pygal/graph/stackedline.py b/pygal/graph/stackedline.py index 6329421..9b8bddc 100644 --- a/pygal/graph/stackedline.py +++ b/pygal/graph/stackedline.py @@ -49,6 +49,5 @@ class StackedLine(Line): serie.points = [ (x_pos[i], v) for i, v in enumerate(accumulation)] - print serie.points if self.interpolate: serie.interpolated = self._interpolate(accumulation, x_pos)