diff --git a/demo/moulinrouge/tests.py b/demo/moulinrouge/tests.py index e53c47c..7934656 100644 --- a/demo/moulinrouge/tests.py +++ b/demo/moulinrouge/tests.py @@ -159,7 +159,7 @@ def get_test_routes(app): @app.route('/test/interpolate/') def test_interpolate_for(chart): - graph = CHARTS_BY_NAME[chart](interpolate='cubic') + graph = CHARTS_BY_NAME[chart](interpolate='hermite') graph.add('1', [1, 3, 12, 3, 4]) graph.add('2', [7, -4, 10, None, 8, 3, 1]) return graph.render_response() diff --git a/pygal/graph/graph.py b/pygal/graph/graph.py index 13d05f0..18f23d6 100644 --- a/pygal/graph/graph.py +++ b/pygal/graph/graph.py @@ -23,7 +23,7 @@ Commmon graphing functions from __future__ import division from pygal.interpolate import ( - quadratic_interpolate, cubic_interpolate) + quadratic_interpolate, cubic_interpolate, hermite_interpolate) from pygal.graph.base import BaseGraph from pygal.view import View, LogView, XYLogView from pygal.util import ( @@ -388,6 +388,8 @@ class Graph(BaseGraph): interpolate = cubic_interpolate if self.interpolate == 'quadratic': interpolate = quadratic_interpolate + elif self.interpolate == 'hermite': + interpolate = hermite_interpolate return list(interpolate(x, y, self.interpolation_precision)) diff --git a/pygal/interpolate.py b/pygal/interpolate.py index f0a627e..00483b8 100644 --- a/pygal/interpolate.py +++ b/pygal/interpolate.py @@ -92,6 +92,38 @@ def cubic_interpolate(x, y, precision=250): yield x[i] + X, a[i] + b[i] * X + c[i] * X2 + d[i] * X3 +def hermite_interpolate(x, y, precision=250): + n = len(x) - 1 + m = [1] * (n + 1) + c = 0 + delta_x = [x2 - x1 for x1, x2 in zip(x, x[1:])] + + for i in range(1, n): + m[i] = (1 - c) * (y[i + 1] - y[i - 1]) / (x[i + 1] - x[i - 1]) + + def p(i, x_): + t = (x_ - x[i]) / delta_x[i] + t2 = t * t + t3 = t2 * t + + h00 = 2 * t3 - 3 * t2 + 1 + h10 = t3 - 2 * t2 + t + h01 = - 2 * t3 + 3 * t2 + h11 = t3 - t2 + + return (h00 * y[i] + + h10 * m[i] * delta_x[i] + + h01 * y[i + 1] + + h11 * m[i + 1] * delta_x[i]) + + for i in range(n + 1): + yield x[i], y[i] + if i == n or delta_x[i] == 0: + continue + for s in range(1, precision): + X = x[i] + s * delta_x[i] / precision + yield X, p(i, X) + if __name__ == '__main__': from pygal import XY points = [(.1, 7), (.3, -4), (.6, 10), (.9, 8), (1.4, 3), (1.7, 1)]